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
239 Log.i(TAG, "Connect starting");
241 Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
242 tmp = (BluetoothSocket) m.invoke(device, 1);
243 // tmp = device.createRfcommSocket(1);
245 Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
246 tmp = (BluetoothSocket) m.invoke(device, 1);
247 // tmp = device.createInsecureRfcommSocket(1);
249 Log.i(TAG, "Connect succeeded");
250 } catch (Exception e) {
251 Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
258 Log.i(TAG, "BEGIN ya y a mConnectThread SocketType:" + mSocketType);
259 setName("ConnectThread" + mSocketType);
261 // Always cancel discovery because it will slow down a connection
262 // mAdapter.cancelDiscovery();
264 // Make a connection to the BluetoothSocket
266 // This is a blocking call and will only return on a
267 // successful connection or an exception
268 Log.i(TAG, "CONNECT SocketType:" + mSocketType);
270 } catch (IOException e) {
272 Log.e(TAG, "Connect failed", e);
275 } catch (IOException e2) {
276 Log.e(TAG, "unable to close() " + mSocketType +
277 " socket during connection failure", e2);
283 // Reset the ConnectThread because we're done
284 synchronized (BluetoothChatService.this) {
285 mConnectThread = null;
288 // Start the connected thread
289 connected(mmSocket, mmDevice, mSocketType);
292 public void cancel() {
295 } catch (IOException e) {
296 Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
302 * This thread runs during a connection with a remote device.
303 * It handles all incoming and outgoing transmissions.
305 private class ConnectedThread extends Thread {
306 private final BluetoothSocket mmSocket;
307 private final InputStream mmInStream;
308 private final OutputStream mmOutStream;
310 public ConnectedThread(BluetoothSocket socket, String socketType) {
311 Log.d(TAG, "create ConnectedThread: " + socketType);
313 InputStream tmpIn = null;
314 OutputStream tmpOut = null;
316 // Get the BluetoothSocket input and output streams
318 tmpIn = socket.getInputStream();
319 tmpOut = socket.getOutputStream();
320 } catch (IOException e) {
321 Log.e(TAG, "temp sockets not created", e);
325 mmOutStream = tmpOut;
329 Log.i(TAG, "BEGIN mConnectedThread");
330 byte[] buffer = new byte[1024];
333 // Keep listening to the InputStream while connected
336 // Read from the InputStream
337 bytes = mmInStream.read(buffer);
340 Log.i(TAG, "Recv: " + new String(buffer, 0, bytes));
341 // Send the obtained bytes to the UI Activity
342 mHandler.obtainMessage(AltosDroid.MESSAGE_READ, bytes, -1, buffer)
345 } catch (IOException e) {
346 Log.e(TAG, "disconnected", e);
354 * Write to the connected OutStream.
355 * @param buffer The bytes to write
357 public void write(byte[] buffer) {
359 mmOutStream.write(buffer);
361 // Share the sent message back to the UI Activity
362 mHandler.obtainMessage(AltosDroid.MESSAGE_WRITE, -1, -1, buffer)
364 } catch (IOException e) {
365 Log.e(TAG, "Exception during write", e);
369 public void cancel() {
372 } catch (IOException e) {
373 Log.e(TAG, "close() of connect socket failed", e);