altosdroid: Add USB support for TeleDongle/TeleBT
[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 import android.util.Log;
27
28 import org.altusmetrum.altoslib_6.*;
29
30 public abstract class AltosDroidLink extends AltosLink {
31
32         // Debugging
33         private static final String TAG = "AltosDroidLink";
34         private static final boolean D = true;
35
36         Handler         handler;
37
38         Thread          input_thread   = null;
39
40         public double frequency() {
41                 return frequency;
42         }
43
44         public int telemetry_rate() {
45                 return telemetry_rate;
46         }
47
48         public void save_frequency() {
49                 AltosPreferences.set_frequency(0, frequency);
50         }
51
52         public void save_telemetry_rate() {
53                 AltosPreferences.set_telemetry_rate(0, telemetry_rate);
54         }
55
56         Object closed_lock = new Object();
57         boolean closing = false;
58         boolean closed = false;
59
60         public boolean closed() {
61                 synchronized(closed_lock) {
62                         return closing;
63                 }
64         }
65
66         void connected() throws InterruptedException {
67                 input_thread = new Thread(this);
68                 input_thread.start();
69
70                 // Configure the newly connected device for telemetry
71                 print("~\nE 0\n");
72                 set_monitor(false);
73                 if (D) Log.d(TAG, "ConnectThread: connected");
74
75                 /* Let TelemetryService know we're connected
76                  */
77                 handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
78
79                 /* Notify other waiting threads that we're connected now
80                  */
81                 notifyAll();
82         }
83
84         public void closing() {
85                 synchronized(closed_lock) {
86                         if (D) Log.d(TAG, "Marked closing true");
87                         closing = true;
88                 }
89         }
90
91         private boolean actually_closed() {
92                 synchronized(closed_lock) {
93                         return closed;
94                 }
95         }
96
97         abstract void close_device();
98
99         public void close() {
100                 if (D) Log.d(TAG, "close(): begin");
101
102                 closing();
103
104                 flush_output();
105
106                 synchronized (closed_lock) {
107                         if (D) Log.d(TAG, "Marked closed true");
108                         closed = true;
109                 }
110
111                 close_device();
112
113                 synchronized(this) {
114
115                         if (input_thread != null) {
116                                 if (D) Log.d(TAG, "close(): stopping input_thread");
117                                 try {
118                                         if (D) Log.d(TAG, "close(): input_thread.interrupt().....");
119                                         input_thread.interrupt();
120                                         if (D) Log.d(TAG, "close(): input_thread.join().....");
121                                         input_thread.join();
122                                 } catch (Exception e) {}
123                                 input_thread = null;
124                         }
125                         notifyAll();
126                 }
127         }
128
129         abstract int write(byte[] buffer, int len);
130
131         abstract int read(byte[] buffer, int len);
132
133         private static final int buffer_size = 64;
134
135         private byte[] in_buffer = new byte[buffer_size];
136         private byte[] out_buffer = new byte[buffer_size];
137         private int buffer_len = 0;
138         private int buffer_off = 0;
139         private int out_buffer_off = 0;
140
141         private byte[] debug_chars = new byte[buffer_size];
142         private int debug_off;
143
144         private void debug_input(byte b) {
145                 if (b == '\n') {
146                         Log.d(TAG, "            " + new String(debug_chars, 0, debug_off));
147                         debug_off = 0;
148                 } else {
149                         if (debug_off < buffer_size)
150                                 debug_chars[debug_off++] = b;
151                 }
152         }
153
154         private void disconnected() {
155                 if (closed()) {
156                         if (D) Log.d(TAG, "disconnected after closed");
157                         return;
158                 }
159
160                 if (D) Log.d(TAG, "Sending disconnected message");
161                 handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
162         }
163
164         public int getchar() {
165
166                 if (actually_closed())
167                         return ERROR;
168
169                 while (buffer_off == buffer_len) {
170                         buffer_len = read(in_buffer, buffer_size);
171                         if (buffer_len < 0) {
172                                 Log.d(TAG, "ERROR returned from getchar()");
173                                 disconnected();
174                                 return ERROR;
175                         }
176                         buffer_off = 0;
177                 }
178                 if (D)
179                         debug_input(in_buffer[buffer_off]);
180                 return in_buffer[buffer_off++];
181         }
182
183         public void flush_output() {
184                 super.flush_output();
185
186                 if (actually_closed()) {
187                         out_buffer_off = 0;
188                         return;
189                 }
190
191                 while (out_buffer_off != 0) {
192                         int     sent = write(out_buffer, out_buffer_off);
193
194                         if (sent <= 0) {
195                                 Log.d(TAG, "flush_output() failed");
196                                 out_buffer_off = 0;
197                                 break;
198                         }
199
200                         if (sent < out_buffer_off)
201                                 System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
202
203                         out_buffer_off -= sent;
204                 }
205         }
206
207         public void putchar(byte c) {
208                 out_buffer[out_buffer_off++] = c;
209                 if (out_buffer_off == buffer_size)
210                         flush_output();
211         }
212
213         public void print(String data) {
214                 byte[] bytes = data.getBytes();
215                 if (D) Log.d(TAG, "print(): begin");
216                 for (byte b : bytes)
217                         putchar(b);
218                 if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
219         }
220
221         public AltosDroidLink(Handler handler) {
222                 this.handler = handler;
223         }
224 }