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.util.UUID;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothSocket;
26 import android.content.Context;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.util.Log;
33 * This class does all the work for setting up and managing Bluetooth
34 * connections with other devices. It has a thread that listens for
35 * incoming connections, a thread for connecting with a device, and a
36 * thread for performing data transmissions when connected.
38 public class BluetoothChatService {
40 private static final String TAG = "BluetoothChatService";
41 private static final boolean D = true;
44 private final BluetoothAdapter mAdapter;
45 private final Handler mHandler;
46 private ConnectThread mConnectThread;
47 private ConnectedThread mConnectedThread;
50 // Constants that indicate the current connection state
51 public static final int STATE_NONE = 0; // we're doing nothing
52 public static final int STATE_READY = 1;
53 public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
54 public static final int STATE_CONNECTED = 3; // now connected to a remote device
57 * Constructor. Prepares a new BluetoothChat session.
58 * @param context The UI Activity Context
59 * @param handler A Handler to send messages back to the UI Activity
61 public BluetoothChatService(Context context, Handler handler) {
62 mAdapter = BluetoothAdapter.getDefaultAdapter();
68 * Set the current state of the chat connection
69 * @param state An integer defining the current connection state
71 private synchronized void setState(int state) {
72 if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
75 // Give the new state to the Handler so the UI Activity can update
76 mHandler.obtainMessage(AltosDroid.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
80 * Return the current connection state. */
81 public synchronized int getState() {
86 * Start the chat service. Specifically start AcceptThread to begin a
87 * session in listening (server) mode. Called by the Activity onResume() */
88 public synchronized void start() {
89 if (D) Log.d(TAG, "start");
91 // Cancel any thread attempting to make a connection
92 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
94 // Cancel any thread currently running a connection
95 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
97 setState(STATE_READY);
102 * Start the ConnectThread to initiate a connection to a remote device.
103 * @param device The BluetoothDevice to connect
105 public synchronized void connect(BluetoothDevice device) {
106 if (D) Log.d(TAG, "connect to: " + device);
108 // Cancel any thread attempting to make a connection
109 if (mState == STATE_CONNECTING) {
110 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
113 // Cancel any thread currently running a connection
114 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
116 // Start the thread to connect with the given device
117 mConnectThread = new ConnectThread(device);
118 mConnectThread.start();
119 setState(STATE_CONNECTING);
123 * Start the ConnectedThread to begin managing a Bluetooth connection
124 * @param socket The BluetoothSocket on which the connection was made
125 * @param device The BluetoothDevice that has been connected
127 public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
128 if (D) Log.d(TAG, "connected");
130 // Cancel the thread that completed the connection
131 if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
133 // Cancel any thread currently running a connection
134 if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
136 // Start the thread to manage the connection and perform transmissions
137 mConnectedThread = new ConnectedThread(socket);
138 mConnectedThread.start();
140 // Send the name of the connected device back to the UI Activity
141 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_DEVICE_NAME);
142 Bundle bundle = new Bundle();
143 bundle.putString(AltosDroid.DEVICE_NAME, device.getName());
145 mHandler.sendMessage(msg);
147 setState(STATE_CONNECTED);
153 public synchronized void stop() {
154 if (D) Log.d(TAG, "stop");
156 if (mConnectThread != null) {
157 mConnectThread.cancel();
158 mConnectThread = null;
161 if (mConnectedThread != null) {
162 mConnectedThread.cancel();
163 mConnectedThread = null;
166 setState(STATE_NONE);
170 * Write to the ConnectedThread in an unsynchronized manner
171 * @param out The bytes to write
172 * @see ConnectedThread#write(byte[])
174 public void write(byte[] out) {
175 // Create temporary object
177 // Synchronize a copy of the ConnectedThread
178 synchronized (this) {
179 if (mState != STATE_CONNECTED) return;
180 r = mConnectedThread;
182 // Perform the write unsynchronized
187 * Indicate that the connection attempt failed and notify the UI Activity.
189 private void connectionFailed() {
190 // Send a failure message back to the Activity
191 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST);
192 Bundle bundle = new Bundle();
193 bundle.putString(AltosDroid.TOAST, "Unable to connect device");
195 mHandler.sendMessage(msg);
197 // Start the service over to restart listening mode
198 BluetoothChatService.this.start();
202 * Indicate that the connection was lost and notify the UI Activity.
204 private void connectionLost() {
205 // Send a failure message back to the Activity
206 Message msg = mHandler.obtainMessage(AltosDroid.MESSAGE_TOAST);
207 Bundle bundle = new Bundle();
208 bundle.putString(AltosDroid.TOAST, "Device connection was lost");
210 mHandler.sendMessage(msg);
212 // Start the service over to restart listening mode
213 BluetoothChatService.this.start();
218 * This thread runs while attempting to make an outgoing connection
219 * with a device. It runs straight through; the connection either
222 private class ConnectThread extends Thread {
223 private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
224 private final BluetoothSocket mmSocket;
225 private final BluetoothDevice mmDevice;
227 public ConnectThread(BluetoothDevice device) {
229 BluetoothSocket tmp = null;
232 tmp = mmDevice.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
233 } catch (IOException e) {
240 Log.i(TAG, "BEGIN mConnectThread");
241 setName("ConnectThread");
243 // Always cancel discovery because it will slow down a connection
244 mAdapter.cancelDiscovery();
246 // Make a connection to the BluetoothSocket
248 // This is a blocking call and will only return on a
249 // successful connection or an exception
251 } catch (IOException e) {
255 } catch (IOException e2) {
256 Log.e(TAG, "unable to close() socket during connection failure", e2);
262 // Reset the ConnectThread because we're done
263 synchronized (BluetoothChatService.this) {
264 mConnectThread = null;
267 // Start the connected thread
268 connected(mmSocket, mmDevice);
271 public void cancel() {
274 } catch (IOException e) {
275 Log.e(TAG, "close() of connect socket failed", e);
281 * This thread runs during a connection with a remote device.
282 * It handles all incoming and outgoing transmissions.
284 private class ConnectedThread extends Thread {
285 private final BluetoothSocket mmSocket;
286 private final InputStream mmInStream;
287 private final OutputStream mmOutStream;
289 public ConnectedThread(BluetoothSocket socket) {
290 Log.d(TAG, "create ConnectedThread");
292 InputStream tmpIn = null;
293 OutputStream tmpOut = null;
295 // Get the BluetoothSocket input and output streams
297 tmpIn = socket.getInputStream();
298 tmpOut = socket.getOutputStream();
299 } catch (IOException e) {
300 Log.e(TAG, "temp sockets not created", e);
304 mmOutStream = tmpOut;
308 Log.i(TAG, "BEGIN mConnectedThread");
309 byte[] buffer = new byte[1024];
312 // Keep listening to the InputStream while connected
315 // Read from the InputStream
316 bytes = mmInStream.read(buffer);
318 // Send the obtained bytes to the UI Activity
319 mHandler.obtainMessage(AltosDroid.MESSAGE_READ, bytes, -1, buffer.clone())
321 } catch (IOException e) {
322 Log.e(TAG, "disconnected", e);
330 * Write to the connected OutStream.
331 * @param buffer The bytes to write
333 public void write(byte[] buffer) {
335 mmOutStream.write(buffer);
336 mmOutStream.write('\n');
338 // Share the sent message back to the UI Activity
339 mHandler.obtainMessage(AltosDroid.MESSAGE_WRITE, -1, -1, buffer)
341 } catch (IOException e) {
342 Log.e(TAG, "Exception during write", e);
346 public void cancel() {
349 } catch (IOException e) {
350 Log.e(TAG, "close() of connect socket failed", e);