03ae5cb84a55281bb3fb5046d0809293e06a5f7f
[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
33 import org.altusmetrum.altoslib_7.*;
34
35 public class AltosBluetooth extends AltosDroidLink {
36
37         private ConnectThread    connect_thread = null;
38
39         private BluetoothDevice  device;
40         private BluetoothSocket  socket;
41         private InputStream      input;
42         private OutputStream     output;
43         private boolean          pause;
44
45         // Constructor
46         public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
47                 super(handler);
48                 this.device = device;
49                 this.handler = handler;
50                 this.pause = pause;
51
52                 connect_thread = new ConnectThread();
53                 connect_thread.start();
54         }
55
56         void connected() {
57                 if (closed()) {
58                         AltosDebug.debug("connected after closed");
59                         return;
60                 }
61
62                 AltosDebug.check_ui("connected\n");
63                 try {
64                         synchronized(this) {
65                                 if (socket != null) {
66                                         input = socket.getInputStream();
67                                         output = socket.getOutputStream();
68                                         super.connected();
69                                 }
70                         }
71                 } catch (InterruptedException ie) {
72                         connect_failed();
73                 } catch (IOException io) {
74                         connect_failed();
75                 }
76         }
77
78         private void connect_failed() {
79                 if (closed()) {
80                         AltosDebug.debug("connect_failed after closed");
81                         return;
82                 }
83
84                 close_device();
85                 input = null;
86                 output = null;
87                 handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
88                 AltosDebug.error("ConnectThread: Failed to establish connection");
89         }
90
91         void close_device() {
92                 BluetoothSocket tmp_socket;
93
94                 synchronized(this) {
95                         tmp_socket = socket;
96                         socket = null;
97                 }
98
99                 if (tmp_socket != null) {
100                         try {
101                                 tmp_socket.close();
102                         } catch (IOException e) {
103                                 AltosDebug.error("close_socket failed");
104                         }
105                 }
106         }
107
108         public void close() {
109                 super.close();
110                 input = null;
111                 output = null;
112         }
113
114         private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
115
116         private void create_socket(BluetoothDevice  device) {
117
118                 BluetoothSocket tmp_socket = null;
119
120                 AltosDebug.check_ui("create_socket\n");
121                 try {
122                         tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
123                 } catch (IOException e) {
124                         e.printStackTrace();
125                 }
126                 if (socket != null) {
127                         AltosDebug.debug("Socket already allocated %s", socket.toString());
128                         close_device();
129                 }
130                 synchronized (this) {
131                         socket = tmp_socket;
132                 }
133         }
134
135         private class ConnectThread extends Thread {
136
137                 public void run() {
138                         AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
139                         setName("ConnectThread");
140
141                         if (pause) {
142                                 try {
143                                         Thread.sleep(4000);
144                                 } catch (InterruptedException e) {
145                                 }
146                         }
147
148                         create_socket(device);
149                         // Always cancel discovery because it will slow down a connection
150                         try {
151                                 BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
152                         } catch (Exception e) {
153                                 AltosDebug.debug("cancelDiscovery exception %s", e.toString());
154                         }
155
156                         BluetoothSocket local_socket = null;
157
158                         synchronized (AltosBluetooth.this) {
159                                 if (!closed())
160                                         local_socket = socket;
161                         }
162
163                         if (local_socket != null) {
164                                 try {
165                                         // Make a connection to the BluetoothSocket
166                                         // This is a blocking call and will only return on a
167                                         // successful connection or an exception
168                                         local_socket.connect();
169                                 } catch (Exception e) {
170                                         AltosDebug.debug("Connect exception %s", e.toString());
171                                         try {
172                                                 local_socket.close();
173                                         } catch (Exception ce) {
174                                                 AltosDebug.debug("Close exception %s", ce.toString());
175                                         }
176                                         local_socket = null;
177                                 }
178                         }
179
180                         if (local_socket != null) {
181                                 connected();
182                         } else {
183                                 connect_failed();
184                         }
185
186                         AltosDebug.debug("ConnectThread: completed");
187                 }
188         }
189
190         private synchronized void wait_connected() throws InterruptedException, IOException {
191                 AltosDebug.check_ui("wait_connected\n");
192                 if (input == null && socket != null) {
193                         AltosDebug.debug("wait_connected...");
194                         wait();
195                         AltosDebug.debug("wait_connected done");
196                 }
197                 if (socket == null)
198                         throw new IOException();
199         }
200
201         int write(byte[] buffer, int len) {
202                 try {
203                         output.write(buffer, 0, len);
204                 } catch (IOException ie) {
205                         return -1;
206                 }
207                 return len;
208         }
209
210         int read(byte[] buffer, int len) {
211                 try {
212                         return input.read(buffer, 0, len);
213                 } catch (IOException ie) {
214                         return -1;
215                 }
216         }
217
218         // Stubs of required methods when extending AltosLink
219         public boolean can_cancel_reply()   { return false; }
220         public boolean show_reply_timeout() { return true; }
221         public void hide_reply_timeout()    { }
222
223 }