Update java library version numbers
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosDroidLink.java
1 /*
2  * Copyright © 2015 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.AltosDroid;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.util.UUID;
24
25 import android.os.Handler;
26
27 import org.altusmetrum.altoslib_10.*;
28
29 public abstract class AltosDroidLink extends AltosLink {
30
31         Handler         handler;
32
33         Thread          input_thread   = null;
34
35         public double frequency() {
36                 return frequency;
37         }
38
39         public int telemetry_rate() {
40                 return telemetry_rate;
41         }
42
43         public void save_frequency() {
44                 AltosPreferences.set_frequency(0, frequency);
45         }
46
47         public void save_telemetry_rate() {
48                 AltosPreferences.set_telemetry_rate(0, telemetry_rate);
49         }
50
51         Object closed_lock = new Object();
52         boolean closing = false;
53         boolean closed = false;
54
55         public boolean closed() {
56                 synchronized(closed_lock) {
57                         return closing;
58                 }
59         }
60
61         void connected() throws InterruptedException {
62                 input_thread = new Thread(this);
63                 input_thread.start();
64
65                 // Configure the newly connected device for telemetry
66                 print("~\nE 0\n");
67                 set_monitor(false);
68                 AltosDebug.debug("ConnectThread: connected");
69
70                 /* Let TelemetryService know we're connected
71                  */
72                 handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
73
74                 /* Notify other waiting threads that we're connected now
75                  */
76                 notifyAll();
77         }
78
79         public void closing() {
80                 synchronized(closed_lock) {
81                         AltosDebug.debug("Marked closing true");
82                         closing = true;
83                 }
84         }
85
86         private boolean actually_closed() {
87                 synchronized(closed_lock) {
88                         return closed;
89                 }
90         }
91
92         abstract void close_device();
93
94         public void close() {
95                 AltosDebug.debug("close(): begin");
96
97                 closing();
98
99                 flush_output();
100
101                 synchronized (closed_lock) {
102                         AltosDebug.debug("Marked closed true");
103                         closed = true;
104                 }
105
106                 close_device();
107
108                 synchronized(this) {
109
110                         if (input_thread != null) {
111                                 AltosDebug.debug("close(): stopping input_thread");
112                                 try {
113                                         AltosDebug.debug("close(): input_thread.interrupt().....");
114                                         input_thread.interrupt();
115                                         AltosDebug.debug("close(): input_thread.join().....");
116                                         input_thread.join();
117                                 } catch (Exception e) {}
118                                 input_thread = null;
119                         }
120                         notifyAll();
121                 }
122         }
123
124         abstract int write(byte[] buffer, int len);
125
126         abstract int read(byte[] buffer, int len);
127
128         private static final int buffer_size = 64;
129
130         private byte[] in_buffer = new byte[buffer_size];
131         private byte[] out_buffer = new byte[buffer_size];
132         private int buffer_len = 0;
133         private int buffer_off = 0;
134         private int out_buffer_off = 0;
135
136         private byte[] debug_chars = new byte[buffer_size];
137         private int debug_off;
138
139         private void debug_input(byte b) {
140                 if (b == '\n') {
141                         AltosDebug.debug("            " + new String(debug_chars, 0, debug_off));
142                         debug_off = 0;
143                 } else {
144                         if (debug_off < buffer_size)
145                                 debug_chars[debug_off++] = b;
146                 }
147         }
148
149         private void disconnected() {
150                 if (closed()) {
151                         AltosDebug.debug("disconnected after closed");
152                         return;
153                 }
154
155                 AltosDebug.debug("Sending disconnected message");
156                 handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
157         }
158
159         public int getchar() {
160
161                 if (actually_closed())
162                         return ERROR;
163
164                 while (buffer_off == buffer_len) {
165                         buffer_len = read(in_buffer, buffer_size);
166                         if (buffer_len < 0) {
167                                 AltosDebug.debug("ERROR returned from getchar()");
168                                 disconnected();
169                                 return ERROR;
170                         }
171                         buffer_off = 0;
172                 }
173                 if (AltosDebug.D)
174                         debug_input(in_buffer[buffer_off]);
175                 return in_buffer[buffer_off++];
176         }
177
178         public void flush_output() {
179                 super.flush_output();
180
181                 if (actually_closed()) {
182                         out_buffer_off = 0;
183                         return;
184                 }
185
186                 while (out_buffer_off != 0) {
187                         int     sent = write(out_buffer, out_buffer_off);
188
189                         if (sent <= 0) {
190                                 AltosDebug.debug("flush_output() failed");
191                                 out_buffer_off = 0;
192                                 break;
193                         }
194
195                         if (sent < out_buffer_off)
196                                 System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
197
198                         out_buffer_off -= sent;
199                 }
200         }
201
202         public void putchar(byte c) {
203                 out_buffer[out_buffer_off++] = c;
204                 if (out_buffer_off == buffer_size)
205                         flush_output();
206         }
207
208         public void print(String data) {
209                 byte[] bytes = data.getBytes();
210                 AltosDebug.debug("print(): begin");
211                 for (byte b : bytes)
212                         putchar(b);
213                 AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
214         }
215
216         public AltosDroidLink(Handler handler) {
217                 this.handler = handler;
218         }
219 }