altosdroid: Add offline map tab
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosBluetooth.java
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  * Copyright © 2012 Mike Beattie <mike@ethernal.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.AltosDroid;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.util.UUID;
25
26 import android.bluetooth.BluetoothAdapter;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothSocket;
29 //import android.os.Bundle;
30 import android.os.Handler;
31 //import android.os.Message;
32 import android.util.Log;
33
34 import org.altusmetrum.altoslib_7.*;
35
36 public class AltosBluetooth extends AltosDroidLink {
37
38         // Debugging
39         private static final String TAG = "AltosBluetooth";
40         private static final boolean D = true;
41
42         private ConnectThread    connect_thread = null;
43
44         private BluetoothAdapter adapter;
45         private BluetoothSocket  socket;
46         private InputStream      input;
47         private OutputStream     output;
48
49         // Constructor
50         public AltosBluetooth(BluetoothDevice device, Handler handler) {
51                 super(handler);
52 //              set_debug(D);
53                 adapter = BluetoothAdapter.getDefaultAdapter();
54                 this.handler = handler;
55
56                 create_socket(device);
57                 connect_thread = new ConnectThread();
58                 connect_thread.start();
59         }
60
61         void connected() {
62                 if (closed()) {
63                         if (D) Log.d(TAG, "connected after closed");
64                         return;
65                 }
66
67                 try {
68                         synchronized(this) {
69                                 if (socket != null) {
70                                         input = socket.getInputStream();
71                                         output = socket.getOutputStream();
72                                         super.connected();
73                                 }
74                         }
75                 } catch (InterruptedException ie) {
76                         connect_failed();
77                 } catch (IOException io) {
78                         connect_failed();
79                 }
80         }
81
82         private void connect_failed() {
83                 if (closed()) {
84                         if (D) Log.d(TAG, "connect_failed after closed");
85                         return;
86                 }
87
88                 close_device();
89                 input = null;
90                 output = null;
91                 handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
92                 if (D) Log.e(TAG, "ConnectThread: Failed to establish connection");
93         }
94
95         void close_device() {
96                 BluetoothSocket tmp_socket;
97
98                 synchronized(this) {
99                         tmp_socket = socket;
100                         socket = null;
101                 }
102
103                 if (tmp_socket != null) {
104                         try {
105                                 tmp_socket.close();
106                         } catch (IOException e) {
107                                 if (D) Log.e(TAG, "close_socket failed");
108                         }
109                 }
110         }
111
112         public void close() {
113                 super.close();
114                 input = null;
115                 output = null;
116         }
117
118         private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
119
120         private void create_socket(BluetoothDevice  device) {
121
122                 BluetoothSocket tmp_socket = null;
123
124                 try {
125                         tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
126                 } catch (IOException e) {
127                         e.printStackTrace();
128                 }
129                 if (socket != null) {
130                         if (D) Log.d(TAG, String.format("Socket already allocated %s", socket.toString()));
131                         close_device();
132                 }
133                 synchronized (this) {
134                         socket = tmp_socket;
135                 }
136         }
137
138         private class ConnectThread extends Thread {
139
140                 public void run() {
141                         if (D) Log.d(TAG, "ConnectThread: BEGIN");
142                         setName("ConnectThread");
143
144                         // Always cancel discovery because it will slow down a connection
145                         try {
146                                 adapter.cancelDiscovery();
147                         } catch (Exception e) {
148                                 if (D) Log.d(TAG, String.format("cancelDiscovery exception %s", e.toString()));
149                         }
150
151                         BluetoothSocket local_socket = null;
152
153                         synchronized (AltosBluetooth.this) {
154                                 if (!closed())
155                                         local_socket = socket;
156                         }
157
158                         if (local_socket != null) {
159                                 try {
160                                         // Make a connection to the BluetoothSocket
161                                         // This is a blocking call and will only return on a
162                                         // successful connection or an exception
163                                         local_socket.connect();
164                                 } catch (IOException e) {
165                                         if (D) Log.d(TAG, String.format("Connect exception %s", e.toString()));
166                                         local_socket = null;
167                                 }
168                         }
169
170                         if (local_socket != null) {
171                                 connected();
172                         } else {
173                                 connect_failed();
174                         }
175
176                         if (D) Log.d(TAG, "ConnectThread: completed");
177                 }
178         }
179
180         private synchronized void wait_connected() throws InterruptedException, IOException {
181                 if (input == null && socket != null) {
182                         if (D) Log.d(TAG, "wait_connected...");
183                         wait();
184                         if (D) Log.d(TAG, "wait_connected done");
185                 }
186                 if (socket == null)
187                         throw new IOException();
188         }
189
190         int write(byte[] buffer, int len) {
191                 try {
192                         output.write(buffer, 0, len);
193                 } catch (IOException ie) {
194                         return -1;
195                 }
196                 return len;
197         }
198
199         int read(byte[] buffer, int len) {
200                 try {
201                         return input.read(buffer, 0, len);
202                 } catch (IOException ie) {
203                         return -1;
204                 }
205         }
206
207         // Stubs of required methods when extending AltosLink
208         public boolean can_cancel_reply()   { return false; }
209         public boolean show_reply_timeout() { return true; }
210         public void hide_reply_timeout()    { }
211
212 }