2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.altusmetrum.AltosDroid;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 //import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothSocket;
28 import android.content.Context;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.util.Log;
35 * This class does all the work for setting up and managing Bluetooth
36 * connections with other devices. It has a thread that listens for
37 * incoming connections, a thread for connecting with a device, and a
38 * thread for performing data transmissions when connected.
40 public class BluetoothChatService {
42 private static final String TAG = "BluetoothChatService";
43 private static final boolean D = true;
46 private final BluetoothAdapter mAdapter;
47 private final Handler mHandler;
48 private ConnectThread mConnectThread;
49 private ConnectedThread mConnectedThread;
52 // Constants that indicate the current connection state
53 public static final int STATE_NONE = 0; // we're doing nothing
54 public static final int STATE_READY = 1;
55 public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
56 public static final int STATE_CONNECTED = 3; // now connected to a remote device
59 * Constructor. Prepares a new BluetoothChat session.
60 * @param context The UI Activity Context
61 * @param handler A Handler to send messages back to the UI Activity
63 public BluetoothChatService(Context context, Handler handler) {
64 mAdapter = BluetoothAdapter.getDefaultAdapter();
70 * Set the current state of the chat connection
71 * @param state An integer defining the current connection state
73 private synchronized void setState(int state) {
74 if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
77 // Give the new state to the Handler so the UI Activity can update
78 mHandler.obtainMessage(AltosDroid.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
82 * Return the current connection state. */
83 public synchronized int getState() {
88 * Start the chat service. Specifically start AcceptThread to begin a
89 * session in listening (server) mode. Called by the Activity onResume() */
90 public synchronized void start() {
91 if (D) Log.d(TAG, "start");
93 // Cancel any thread attempting to make a connection
94 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
96 // Cancel any thread currently running a connection
97 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
99 setState(STATE_READY);
104 * Start the ConnectThread to initiate a connection to a remote device.
105 * @param device The BluetoothDevice to connect
106 * @param secure Socket Security type - Secure (true) , Insecure (false)
108 public synchronized void connect(BluetoothDevice device, boolean secure) {
109 if (D) Log.d(TAG, "connect to: " + device);
111 // Cancel any thread attempting to make a connection
112 if (mState == STATE_CONNECTING) {
113 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
116 // Cancel any thread currently running a connection
117 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
119 // Start the thread to connect with the given device
120 mConnectThread = new ConnectThread(device, secure);
121 mConnectThread.start();
122 setState(STATE_CONNECTING);
126 * Start the ConnectedThread to begin managing a Bluetooth connection
127 * @param socket The BluetoothSocket on which the connection was made
128 * @param device The BluetoothDevice that has been connected
130 public synchronized void connected(BluetoothSocket socket, BluetoothDevice
131 device, final String socketType) {
132 if (D) Log.d(TAG, "connected, Socket Type:" + socketType);
134 // Cancel the thread that completed the connection
135 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
137 // Cancel any thread currently running a connection
138 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
140 // Start the thread to manage the connection and perform transmissions
141 mConnectedThread = new ConnectedThread(socket, socketType);
142 mConnectedThread.start();
144 // Send the name of the connected device back to the UI Activity
145 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_DEVICE_NAME);
146 Bundle bundle = new Bundle();
147 bundle.putString(AltosDroid.DEVICE_NAME, device.getName());
149 mHandler.sendMessage(msg);
151 setState(STATE_CONNECTED);
157 public synchronized void stop() {
158 if (D) Log.d(TAG, "stop");
160 if (mConnectThread != null) {
161 mConnectThread.cancel();
162 mConnectThread = null;
165 if (mConnectedThread != null) {
166 mConnectedThread.cancel();
167 mConnectedThread = null;
170 setState(STATE_NONE);
174 * Write to the ConnectedThread in an unsynchronized manner
175 * @param out The bytes to write
176 * @see ConnectedThread#write(byte[])
178 public void write(byte[] out) {
179 // Create temporary object
181 // Synchronize a copy of the ConnectedThread
182 synchronized (this) {
183 if (mState != STATE_CONNECTED) return;
184 r = mConnectedThread;
186 // Perform the write unsynchronized
191 * Indicate that the connection attempt failed and notify the UI Activity.
193 private void connectionFailed() {
194 // Send a failure message back to the Activity
195 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST);
196 Bundle bundle = new Bundle();
197 bundle.putString(AltosDroid.TOAST, "Unable to connect device");
199 mHandler.sendMessage(msg);
201 // Start the service over to restart listening mode
202 BluetoothChatService.this.start();
206 * Indicate that the connection was lost and notify the UI Activity.
208 private void connectionLost() {
209 // Send a failure message back to the Activity
210 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST);
211 Bundle bundle = new Bundle();
212 bundle.putString(AltosDroid.TOAST, "Device connection was lost");
214 mHandler.sendMessage(msg);
216 // Start the service over to restart listening mode
217 BluetoothChatService.this.start();
222 * This thread runs while attempting to make an outgoing connection
223 * with a device. It runs straight through; the connection either
226 private class ConnectThread extends Thread {
227 private final BluetoothSocket mmSocket;
228 private final BluetoothDevice mmDevice;
229 private String mSocketType;
231 public ConnectThread(BluetoothDevice device, boolean secure) {
233 BluetoothSocket tmp = null;
234 mSocketType = secure ? "Secure" : "Insecure";
236 // Get a BluetoothSocket for a connection with the
237 // given BluetoothDevice
240 Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
241 tmp = (BluetoothSocket) m.invoke(device, 2);
242 // tmp = device.createRfcommSocket(1);
244 Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
245 tmp = (BluetoothSocket) m.invoke(device, 2);
246 // tmp = device.createInsecureRfcommSocket(1);
248 } catch (Exception e) {
249 Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
256 Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
257 setName("ConnectThread" + mSocketType);
259 // Always cancel discovery because it will slow down a connection
260 mAdapter.cancelDiscovery();
262 // Make a connection to the BluetoothSocket
264 // This is a blocking call and will only return on a
265 // successful connection or an exception
267 } catch (IOException e) {
271 } catch (IOException e2) {
272 Log.e(TAG, "unable to close() " + mSocketType +
273 " socket during connection failure", e2);
279 // Reset the ConnectThread because we're done
280 synchronized (BluetoothChatService.this) {
281 mConnectThread = null;
284 // Start the connected thread
285 connected(mmSocket, mmDevice, mSocketType);
288 public void cancel() {
291 } catch (IOException e) {
292 Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
298 * This thread runs during a connection with a remote device.
299 * It handles all incoming and outgoing transmissions.
301 private class ConnectedThread extends Thread {
302 private final BluetoothSocket mmSocket;
303 private final InputStream mmInStream;
304 private final OutputStream mmOutStream;
306 public ConnectedThread(BluetoothSocket socket, String socketType) {
307 Log.d(TAG, "create ConnectedThread: " + socketType);
309 InputStream tmpIn = null;
310 OutputStream tmpOut = null;
312 // Get the BluetoothSocket input and output streams
314 tmpIn = socket.getInputStream();
315 tmpOut = socket.getOutputStream();
316 } catch (IOException e) {
317 Log.e(TAG, "temp sockets not created", e);
321 mmOutStream = tmpOut;
325 Log.i(TAG, "BEGIN mConnectedThread");
326 byte[] buffer = new byte[1024];
329 // Keep listening to the InputStream while connected
332 // Read from the InputStream
333 bytes = mmInStream.read(buffer);
335 // Send the obtained bytes to the UI Activity
336 mHandler.obtainMessage(AltosDroid.MESSAGE_READ, bytes, -1, buffer)
338 } catch (IOException e) {
339 Log.e(TAG, "disconnected", e);
347 * Write to the connected OutStream.
348 * @param buffer The bytes to write
350 public void write(byte[] buffer) {
352 mmOutStream.write(buffer);
354 // Share the sent message back to the UI Activity
355 mHandler.obtainMessage(AltosDroid.MESSAGE_WRITE, -1, -1, buffer)
357 } catch (IOException e) {
358 Log.e(TAG, "Exception during write", e);
362 public void cancel() {
365 } catch (IOException e) {
366 Log.e(TAG, "close() of connect socket failed", e);