1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2014 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.bluetoothchat; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothAdapter; 20*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothDevice; 21*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothServerSocket; 22*90c8c64dSAndroid Build Coastguard Worker import android.bluetooth.BluetoothSocket; 23*90c8c64dSAndroid Build Coastguard Worker import android.content.Context; 24*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 25*90c8c64dSAndroid Build Coastguard Worker import android.os.Handler; 26*90c8c64dSAndroid Build Coastguard Worker import android.os.Message; 27*90c8c64dSAndroid Build Coastguard Worker 28*90c8c64dSAndroid Build Coastguard Worker import com.example.android.common.logger.Log; 29*90c8c64dSAndroid Build Coastguard Worker 30*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 31*90c8c64dSAndroid Build Coastguard Worker import java.io.InputStream; 32*90c8c64dSAndroid Build Coastguard Worker import java.io.OutputStream; 33*90c8c64dSAndroid Build Coastguard Worker import java.util.UUID; 34*90c8c64dSAndroid Build Coastguard Worker 35*90c8c64dSAndroid Build Coastguard Worker /** 36*90c8c64dSAndroid Build Coastguard Worker * This class does all the work for setting up and managing Bluetooth 37*90c8c64dSAndroid Build Coastguard Worker * connections with other devices. It has a thread that listens for 38*90c8c64dSAndroid Build Coastguard Worker * incoming connections, a thread for connecting with a device, and a 39*90c8c64dSAndroid Build Coastguard Worker * thread for performing data transmissions when connected. 40*90c8c64dSAndroid Build Coastguard Worker */ 41*90c8c64dSAndroid Build Coastguard Worker public class BluetoothChatService { 42*90c8c64dSAndroid Build Coastguard Worker // Debugging 43*90c8c64dSAndroid Build Coastguard Worker private static final String TAG = "BluetoothChatService"; 44*90c8c64dSAndroid Build Coastguard Worker 45*90c8c64dSAndroid Build Coastguard Worker // Name for the SDP record when creating server socket 46*90c8c64dSAndroid Build Coastguard Worker private static final String NAME_SECURE = "BluetoothChatSecure"; 47*90c8c64dSAndroid Build Coastguard Worker private static final String NAME_INSECURE = "BluetoothChatInsecure"; 48*90c8c64dSAndroid Build Coastguard Worker 49*90c8c64dSAndroid Build Coastguard Worker // Unique UUID for this application 50*90c8c64dSAndroid Build Coastguard Worker private static final UUID MY_UUID_SECURE = 51*90c8c64dSAndroid Build Coastguard Worker UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); 52*90c8c64dSAndroid Build Coastguard Worker private static final UUID MY_UUID_INSECURE = 53*90c8c64dSAndroid Build Coastguard Worker UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66"); 54*90c8c64dSAndroid Build Coastguard Worker 55*90c8c64dSAndroid Build Coastguard Worker // Member fields 56*90c8c64dSAndroid Build Coastguard Worker private final BluetoothAdapter mAdapter; 57*90c8c64dSAndroid Build Coastguard Worker private final Handler mHandler; 58*90c8c64dSAndroid Build Coastguard Worker private AcceptThread mSecureAcceptThread; 59*90c8c64dSAndroid Build Coastguard Worker private AcceptThread mInsecureAcceptThread; 60*90c8c64dSAndroid Build Coastguard Worker private ConnectThread mConnectThread; 61*90c8c64dSAndroid Build Coastguard Worker private ConnectedThread mConnectedThread; 62*90c8c64dSAndroid Build Coastguard Worker private int mState; 63*90c8c64dSAndroid Build Coastguard Worker private int mNewState; 64*90c8c64dSAndroid Build Coastguard Worker 65*90c8c64dSAndroid Build Coastguard Worker // Constants that indicate the current connection state 66*90c8c64dSAndroid Build Coastguard Worker public static final int STATE_NONE = 0; // we're doing nothing 67*90c8c64dSAndroid Build Coastguard Worker public static final int STATE_LISTEN = 1; // now listening for incoming connections 68*90c8c64dSAndroid Build Coastguard Worker public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection 69*90c8c64dSAndroid Build Coastguard Worker public static final int STATE_CONNECTED = 3; // now connected to a remote device 70*90c8c64dSAndroid Build Coastguard Worker 71*90c8c64dSAndroid Build Coastguard Worker /** 72*90c8c64dSAndroid Build Coastguard Worker * Constructor. Prepares a new BluetoothChat session. 73*90c8c64dSAndroid Build Coastguard Worker * 74*90c8c64dSAndroid Build Coastguard Worker * @param context The UI Activity Context 75*90c8c64dSAndroid Build Coastguard Worker * @param handler A Handler to send messages back to the UI Activity 76*90c8c64dSAndroid Build Coastguard Worker */ BluetoothChatService(Context context, Handler handler)77*90c8c64dSAndroid Build Coastguard Worker public BluetoothChatService(Context context, Handler handler) { 78*90c8c64dSAndroid Build Coastguard Worker mAdapter = BluetoothAdapter.getDefaultAdapter(); 79*90c8c64dSAndroid Build Coastguard Worker mState = STATE_NONE; 80*90c8c64dSAndroid Build Coastguard Worker mNewState = mState; 81*90c8c64dSAndroid Build Coastguard Worker mHandler = handler; 82*90c8c64dSAndroid Build Coastguard Worker } 83*90c8c64dSAndroid Build Coastguard Worker 84*90c8c64dSAndroid Build Coastguard Worker /** 85*90c8c64dSAndroid Build Coastguard Worker * Update UI title according to the current state of the chat connection 86*90c8c64dSAndroid Build Coastguard Worker */ updateUserInterfaceTitle()87*90c8c64dSAndroid Build Coastguard Worker private synchronized void updateUserInterfaceTitle() { 88*90c8c64dSAndroid Build Coastguard Worker mState = getState(); 89*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState); 90*90c8c64dSAndroid Build Coastguard Worker mNewState = mState; 91*90c8c64dSAndroid Build Coastguard Worker 92*90c8c64dSAndroid Build Coastguard Worker // Give the new state to the Handler so the UI Activity can update 93*90c8c64dSAndroid Build Coastguard Worker mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget(); 94*90c8c64dSAndroid Build Coastguard Worker } 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker /** 97*90c8c64dSAndroid Build Coastguard Worker * Return the current connection state. 98*90c8c64dSAndroid Build Coastguard Worker */ getState()99*90c8c64dSAndroid Build Coastguard Worker public synchronized int getState() { 100*90c8c64dSAndroid Build Coastguard Worker return mState; 101*90c8c64dSAndroid Build Coastguard Worker } 102*90c8c64dSAndroid Build Coastguard Worker 103*90c8c64dSAndroid Build Coastguard Worker /** 104*90c8c64dSAndroid Build Coastguard Worker * Start the chat service. Specifically start AcceptThread to begin a 105*90c8c64dSAndroid Build Coastguard Worker * session in listening (server) mode. Called by the Activity onResume() 106*90c8c64dSAndroid Build Coastguard Worker */ start()107*90c8c64dSAndroid Build Coastguard Worker public synchronized void start() { 108*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "start"); 109*90c8c64dSAndroid Build Coastguard Worker 110*90c8c64dSAndroid Build Coastguard Worker // Cancel any thread attempting to make a connection 111*90c8c64dSAndroid Build Coastguard Worker if (mConnectThread != null) { 112*90c8c64dSAndroid Build Coastguard Worker mConnectThread.cancel(); 113*90c8c64dSAndroid Build Coastguard Worker mConnectThread = null; 114*90c8c64dSAndroid Build Coastguard Worker } 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Worker // Cancel any thread currently running a connection 117*90c8c64dSAndroid Build Coastguard Worker if (mConnectedThread != null) { 118*90c8c64dSAndroid Build Coastguard Worker mConnectedThread.cancel(); 119*90c8c64dSAndroid Build Coastguard Worker mConnectedThread = null; 120*90c8c64dSAndroid Build Coastguard Worker } 121*90c8c64dSAndroid Build Coastguard Worker 122*90c8c64dSAndroid Build Coastguard Worker // Start the thread to listen on a BluetoothServerSocket 123*90c8c64dSAndroid Build Coastguard Worker if (mSecureAcceptThread == null) { 124*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread = new AcceptThread(true); 125*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread.start(); 126*90c8c64dSAndroid Build Coastguard Worker } 127*90c8c64dSAndroid Build Coastguard Worker if (mInsecureAcceptThread == null) { 128*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread = new AcceptThread(false); 129*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread.start(); 130*90c8c64dSAndroid Build Coastguard Worker } 131*90c8c64dSAndroid Build Coastguard Worker // Update UI title 132*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 133*90c8c64dSAndroid Build Coastguard Worker } 134*90c8c64dSAndroid Build Coastguard Worker 135*90c8c64dSAndroid Build Coastguard Worker /** 136*90c8c64dSAndroid Build Coastguard Worker * Start the ConnectThread to initiate a connection to a remote device. 137*90c8c64dSAndroid Build Coastguard Worker * 138*90c8c64dSAndroid Build Coastguard Worker * @param device The BluetoothDevice to connect 139*90c8c64dSAndroid Build Coastguard Worker * @param secure Socket Security type - Secure (true) , Insecure (false) 140*90c8c64dSAndroid Build Coastguard Worker */ connect(BluetoothDevice device, boolean secure)141*90c8c64dSAndroid Build Coastguard Worker public synchronized void connect(BluetoothDevice device, boolean secure) { 142*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "connect to: " + device); 143*90c8c64dSAndroid Build Coastguard Worker 144*90c8c64dSAndroid Build Coastguard Worker // Cancel any thread attempting to make a connection 145*90c8c64dSAndroid Build Coastguard Worker if (mState == STATE_CONNECTING) { 146*90c8c64dSAndroid Build Coastguard Worker if (mConnectThread != null) { 147*90c8c64dSAndroid Build Coastguard Worker mConnectThread.cancel(); 148*90c8c64dSAndroid Build Coastguard Worker mConnectThread = null; 149*90c8c64dSAndroid Build Coastguard Worker } 150*90c8c64dSAndroid Build Coastguard Worker } 151*90c8c64dSAndroid Build Coastguard Worker 152*90c8c64dSAndroid Build Coastguard Worker // Cancel any thread currently running a connection 153*90c8c64dSAndroid Build Coastguard Worker if (mConnectedThread != null) { 154*90c8c64dSAndroid Build Coastguard Worker mConnectedThread.cancel(); 155*90c8c64dSAndroid Build Coastguard Worker mConnectedThread = null; 156*90c8c64dSAndroid Build Coastguard Worker } 157*90c8c64dSAndroid Build Coastguard Worker 158*90c8c64dSAndroid Build Coastguard Worker // Start the thread to connect with the given device 159*90c8c64dSAndroid Build Coastguard Worker mConnectThread = new ConnectThread(device, secure); 160*90c8c64dSAndroid Build Coastguard Worker mConnectThread.start(); 161*90c8c64dSAndroid Build Coastguard Worker // Update UI title 162*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 163*90c8c64dSAndroid Build Coastguard Worker } 164*90c8c64dSAndroid Build Coastguard Worker 165*90c8c64dSAndroid Build Coastguard Worker /** 166*90c8c64dSAndroid Build Coastguard Worker * Start the ConnectedThread to begin managing a Bluetooth connection 167*90c8c64dSAndroid Build Coastguard Worker * 168*90c8c64dSAndroid Build Coastguard Worker * @param socket The BluetoothSocket on which the connection was made 169*90c8c64dSAndroid Build Coastguard Worker * @param device The BluetoothDevice that has been connected 170*90c8c64dSAndroid Build Coastguard Worker */ connected(BluetoothSocket socket, BluetoothDevice device, final String socketType)171*90c8c64dSAndroid Build Coastguard Worker public synchronized void connected(BluetoothSocket socket, BluetoothDevice 172*90c8c64dSAndroid Build Coastguard Worker device, final String socketType) { 173*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "connected, Socket Type:" + socketType); 174*90c8c64dSAndroid Build Coastguard Worker 175*90c8c64dSAndroid Build Coastguard Worker // Cancel the thread that completed the connection 176*90c8c64dSAndroid Build Coastguard Worker if (mConnectThread != null) { 177*90c8c64dSAndroid Build Coastguard Worker mConnectThread.cancel(); 178*90c8c64dSAndroid Build Coastguard Worker mConnectThread = null; 179*90c8c64dSAndroid Build Coastguard Worker } 180*90c8c64dSAndroid Build Coastguard Worker 181*90c8c64dSAndroid Build Coastguard Worker // Cancel any thread currently running a connection 182*90c8c64dSAndroid Build Coastguard Worker if (mConnectedThread != null) { 183*90c8c64dSAndroid Build Coastguard Worker mConnectedThread.cancel(); 184*90c8c64dSAndroid Build Coastguard Worker mConnectedThread = null; 185*90c8c64dSAndroid Build Coastguard Worker } 186*90c8c64dSAndroid Build Coastguard Worker 187*90c8c64dSAndroid Build Coastguard Worker // Cancel the accept thread because we only want to connect to one device 188*90c8c64dSAndroid Build Coastguard Worker if (mSecureAcceptThread != null) { 189*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread.cancel(); 190*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread = null; 191*90c8c64dSAndroid Build Coastguard Worker } 192*90c8c64dSAndroid Build Coastguard Worker if (mInsecureAcceptThread != null) { 193*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread.cancel(); 194*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread = null; 195*90c8c64dSAndroid Build Coastguard Worker } 196*90c8c64dSAndroid Build Coastguard Worker 197*90c8c64dSAndroid Build Coastguard Worker // Start the thread to manage the connection and perform transmissions 198*90c8c64dSAndroid Build Coastguard Worker mConnectedThread = new ConnectedThread(socket, socketType); 199*90c8c64dSAndroid Build Coastguard Worker mConnectedThread.start(); 200*90c8c64dSAndroid Build Coastguard Worker 201*90c8c64dSAndroid Build Coastguard Worker // Send the name of the connected device back to the UI Activity 202*90c8c64dSAndroid Build Coastguard Worker Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME); 203*90c8c64dSAndroid Build Coastguard Worker Bundle bundle = new Bundle(); 204*90c8c64dSAndroid Build Coastguard Worker bundle.putString(Constants.DEVICE_NAME, device.getName()); 205*90c8c64dSAndroid Build Coastguard Worker msg.setData(bundle); 206*90c8c64dSAndroid Build Coastguard Worker mHandler.sendMessage(msg); 207*90c8c64dSAndroid Build Coastguard Worker // Update UI title 208*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 209*90c8c64dSAndroid Build Coastguard Worker } 210*90c8c64dSAndroid Build Coastguard Worker 211*90c8c64dSAndroid Build Coastguard Worker /** 212*90c8c64dSAndroid Build Coastguard Worker * Stop all threads 213*90c8c64dSAndroid Build Coastguard Worker */ stop()214*90c8c64dSAndroid Build Coastguard Worker public synchronized void stop() { 215*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "stop"); 216*90c8c64dSAndroid Build Coastguard Worker 217*90c8c64dSAndroid Build Coastguard Worker if (mConnectThread != null) { 218*90c8c64dSAndroid Build Coastguard Worker mConnectThread.cancel(); 219*90c8c64dSAndroid Build Coastguard Worker mConnectThread = null; 220*90c8c64dSAndroid Build Coastguard Worker } 221*90c8c64dSAndroid Build Coastguard Worker 222*90c8c64dSAndroid Build Coastguard Worker if (mConnectedThread != null) { 223*90c8c64dSAndroid Build Coastguard Worker mConnectedThread.cancel(); 224*90c8c64dSAndroid Build Coastguard Worker mConnectedThread = null; 225*90c8c64dSAndroid Build Coastguard Worker } 226*90c8c64dSAndroid Build Coastguard Worker 227*90c8c64dSAndroid Build Coastguard Worker if (mSecureAcceptThread != null) { 228*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread.cancel(); 229*90c8c64dSAndroid Build Coastguard Worker mSecureAcceptThread = null; 230*90c8c64dSAndroid Build Coastguard Worker } 231*90c8c64dSAndroid Build Coastguard Worker 232*90c8c64dSAndroid Build Coastguard Worker if (mInsecureAcceptThread != null) { 233*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread.cancel(); 234*90c8c64dSAndroid Build Coastguard Worker mInsecureAcceptThread = null; 235*90c8c64dSAndroid Build Coastguard Worker } 236*90c8c64dSAndroid Build Coastguard Worker mState = STATE_NONE; 237*90c8c64dSAndroid Build Coastguard Worker // Update UI title 238*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 239*90c8c64dSAndroid Build Coastguard Worker } 240*90c8c64dSAndroid Build Coastguard Worker 241*90c8c64dSAndroid Build Coastguard Worker /** 242*90c8c64dSAndroid Build Coastguard Worker * Write to the ConnectedThread in an unsynchronized manner 243*90c8c64dSAndroid Build Coastguard Worker * 244*90c8c64dSAndroid Build Coastguard Worker * @param out The bytes to write 245*90c8c64dSAndroid Build Coastguard Worker * @see ConnectedThread#write(byte[]) 246*90c8c64dSAndroid Build Coastguard Worker */ write(byte[] out)247*90c8c64dSAndroid Build Coastguard Worker public void write(byte[] out) { 248*90c8c64dSAndroid Build Coastguard Worker // Create temporary object 249*90c8c64dSAndroid Build Coastguard Worker ConnectedThread r; 250*90c8c64dSAndroid Build Coastguard Worker // Synchronize a copy of the ConnectedThread 251*90c8c64dSAndroid Build Coastguard Worker synchronized (this) { 252*90c8c64dSAndroid Build Coastguard Worker if (mState != STATE_CONNECTED) return; 253*90c8c64dSAndroid Build Coastguard Worker r = mConnectedThread; 254*90c8c64dSAndroid Build Coastguard Worker } 255*90c8c64dSAndroid Build Coastguard Worker // Perform the write unsynchronized 256*90c8c64dSAndroid Build Coastguard Worker r.write(out); 257*90c8c64dSAndroid Build Coastguard Worker } 258*90c8c64dSAndroid Build Coastguard Worker 259*90c8c64dSAndroid Build Coastguard Worker /** 260*90c8c64dSAndroid Build Coastguard Worker * Indicate that the connection attempt failed and notify the UI Activity. 261*90c8c64dSAndroid Build Coastguard Worker */ connectionFailed()262*90c8c64dSAndroid Build Coastguard Worker private void connectionFailed() { 263*90c8c64dSAndroid Build Coastguard Worker // Send a failure message back to the Activity 264*90c8c64dSAndroid Build Coastguard Worker Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST); 265*90c8c64dSAndroid Build Coastguard Worker Bundle bundle = new Bundle(); 266*90c8c64dSAndroid Build Coastguard Worker bundle.putString(Constants.TOAST, "Unable to connect device"); 267*90c8c64dSAndroid Build Coastguard Worker msg.setData(bundle); 268*90c8c64dSAndroid Build Coastguard Worker mHandler.sendMessage(msg); 269*90c8c64dSAndroid Build Coastguard Worker 270*90c8c64dSAndroid Build Coastguard Worker mState = STATE_NONE; 271*90c8c64dSAndroid Build Coastguard Worker // Update UI title 272*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 273*90c8c64dSAndroid Build Coastguard Worker 274*90c8c64dSAndroid Build Coastguard Worker // Start the service over to restart listening mode 275*90c8c64dSAndroid Build Coastguard Worker BluetoothChatService.this.start(); 276*90c8c64dSAndroid Build Coastguard Worker } 277*90c8c64dSAndroid Build Coastguard Worker 278*90c8c64dSAndroid Build Coastguard Worker /** 279*90c8c64dSAndroid Build Coastguard Worker * Indicate that the connection was lost and notify the UI Activity. 280*90c8c64dSAndroid Build Coastguard Worker */ connectionLost()281*90c8c64dSAndroid Build Coastguard Worker private void connectionLost() { 282*90c8c64dSAndroid Build Coastguard Worker // Send a failure message back to the Activity 283*90c8c64dSAndroid Build Coastguard Worker Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST); 284*90c8c64dSAndroid Build Coastguard Worker Bundle bundle = new Bundle(); 285*90c8c64dSAndroid Build Coastguard Worker bundle.putString(Constants.TOAST, "Device connection was lost"); 286*90c8c64dSAndroid Build Coastguard Worker msg.setData(bundle); 287*90c8c64dSAndroid Build Coastguard Worker mHandler.sendMessage(msg); 288*90c8c64dSAndroid Build Coastguard Worker 289*90c8c64dSAndroid Build Coastguard Worker mState = STATE_NONE; 290*90c8c64dSAndroid Build Coastguard Worker // Update UI title 291*90c8c64dSAndroid Build Coastguard Worker updateUserInterfaceTitle(); 292*90c8c64dSAndroid Build Coastguard Worker 293*90c8c64dSAndroid Build Coastguard Worker // Start the service over to restart listening mode 294*90c8c64dSAndroid Build Coastguard Worker BluetoothChatService.this.start(); 295*90c8c64dSAndroid Build Coastguard Worker } 296*90c8c64dSAndroid Build Coastguard Worker 297*90c8c64dSAndroid Build Coastguard Worker /** 298*90c8c64dSAndroid Build Coastguard Worker * This thread runs while listening for incoming connections. It behaves 299*90c8c64dSAndroid Build Coastguard Worker * like a server-side client. It runs until a connection is accepted 300*90c8c64dSAndroid Build Coastguard Worker * (or until cancelled). 301*90c8c64dSAndroid Build Coastguard Worker */ 302*90c8c64dSAndroid Build Coastguard Worker private class AcceptThread extends Thread { 303*90c8c64dSAndroid Build Coastguard Worker // The local server socket 304*90c8c64dSAndroid Build Coastguard Worker private final BluetoothServerSocket mmServerSocket; 305*90c8c64dSAndroid Build Coastguard Worker private String mSocketType; 306*90c8c64dSAndroid Build Coastguard Worker AcceptThread(boolean secure)307*90c8c64dSAndroid Build Coastguard Worker public AcceptThread(boolean secure) { 308*90c8c64dSAndroid Build Coastguard Worker BluetoothServerSocket tmp = null; 309*90c8c64dSAndroid Build Coastguard Worker mSocketType = secure ? "Secure" : "Insecure"; 310*90c8c64dSAndroid Build Coastguard Worker 311*90c8c64dSAndroid Build Coastguard Worker // Create a new listening server socket 312*90c8c64dSAndroid Build Coastguard Worker try { 313*90c8c64dSAndroid Build Coastguard Worker if (secure) { 314*90c8c64dSAndroid Build Coastguard Worker tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, 315*90c8c64dSAndroid Build Coastguard Worker MY_UUID_SECURE); 316*90c8c64dSAndroid Build Coastguard Worker } else { 317*90c8c64dSAndroid Build Coastguard Worker tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord( 318*90c8c64dSAndroid Build Coastguard Worker NAME_INSECURE, MY_UUID_INSECURE); 319*90c8c64dSAndroid Build Coastguard Worker } 320*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 321*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); 322*90c8c64dSAndroid Build Coastguard Worker } 323*90c8c64dSAndroid Build Coastguard Worker mmServerSocket = tmp; 324*90c8c64dSAndroid Build Coastguard Worker mState = STATE_LISTEN; 325*90c8c64dSAndroid Build Coastguard Worker } 326*90c8c64dSAndroid Build Coastguard Worker run()327*90c8c64dSAndroid Build Coastguard Worker public void run() { 328*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Socket Type: " + mSocketType + 329*90c8c64dSAndroid Build Coastguard Worker "BEGIN mAcceptThread" + this); 330*90c8c64dSAndroid Build Coastguard Worker setName("AcceptThread" + mSocketType); 331*90c8c64dSAndroid Build Coastguard Worker 332*90c8c64dSAndroid Build Coastguard Worker BluetoothSocket socket = null; 333*90c8c64dSAndroid Build Coastguard Worker 334*90c8c64dSAndroid Build Coastguard Worker // Listen to the server socket if we're not connected 335*90c8c64dSAndroid Build Coastguard Worker while (mState != STATE_CONNECTED) { 336*90c8c64dSAndroid Build Coastguard Worker try { 337*90c8c64dSAndroid Build Coastguard Worker // This is a blocking call and will only return on a 338*90c8c64dSAndroid Build Coastguard Worker // successful connection or an exception 339*90c8c64dSAndroid Build Coastguard Worker socket = mmServerSocket.accept(); 340*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 341*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e); 342*90c8c64dSAndroid Build Coastguard Worker break; 343*90c8c64dSAndroid Build Coastguard Worker } 344*90c8c64dSAndroid Build Coastguard Worker 345*90c8c64dSAndroid Build Coastguard Worker // If a connection was accepted 346*90c8c64dSAndroid Build Coastguard Worker if (socket != null) { 347*90c8c64dSAndroid Build Coastguard Worker synchronized (BluetoothChatService.this) { 348*90c8c64dSAndroid Build Coastguard Worker switch (mState) { 349*90c8c64dSAndroid Build Coastguard Worker case STATE_LISTEN: 350*90c8c64dSAndroid Build Coastguard Worker case STATE_CONNECTING: 351*90c8c64dSAndroid Build Coastguard Worker // Situation normal. Start the connected thread. 352*90c8c64dSAndroid Build Coastguard Worker connected(socket, socket.getRemoteDevice(), 353*90c8c64dSAndroid Build Coastguard Worker mSocketType); 354*90c8c64dSAndroid Build Coastguard Worker break; 355*90c8c64dSAndroid Build Coastguard Worker case STATE_NONE: 356*90c8c64dSAndroid Build Coastguard Worker case STATE_CONNECTED: 357*90c8c64dSAndroid Build Coastguard Worker // Either not ready or already connected. Terminate new socket. 358*90c8c64dSAndroid Build Coastguard Worker try { 359*90c8c64dSAndroid Build Coastguard Worker socket.close(); 360*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 361*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Could not close unwanted socket", e); 362*90c8c64dSAndroid Build Coastguard Worker } 363*90c8c64dSAndroid Build Coastguard Worker break; 364*90c8c64dSAndroid Build Coastguard Worker } 365*90c8c64dSAndroid Build Coastguard Worker } 366*90c8c64dSAndroid Build Coastguard Worker } 367*90c8c64dSAndroid Build Coastguard Worker } 368*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); 369*90c8c64dSAndroid Build Coastguard Worker 370*90c8c64dSAndroid Build Coastguard Worker } 371*90c8c64dSAndroid Build Coastguard Worker cancel()372*90c8c64dSAndroid Build Coastguard Worker public void cancel() { 373*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); 374*90c8c64dSAndroid Build Coastguard Worker try { 375*90c8c64dSAndroid Build Coastguard Worker mmServerSocket.close(); 376*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 377*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); 378*90c8c64dSAndroid Build Coastguard Worker } 379*90c8c64dSAndroid Build Coastguard Worker } 380*90c8c64dSAndroid Build Coastguard Worker } 381*90c8c64dSAndroid Build Coastguard Worker 382*90c8c64dSAndroid Build Coastguard Worker 383*90c8c64dSAndroid Build Coastguard Worker /** 384*90c8c64dSAndroid Build Coastguard Worker * This thread runs while attempting to make an outgoing connection 385*90c8c64dSAndroid Build Coastguard Worker * with a device. It runs straight through; the connection either 386*90c8c64dSAndroid Build Coastguard Worker * succeeds or fails. 387*90c8c64dSAndroid Build Coastguard Worker */ 388*90c8c64dSAndroid Build Coastguard Worker private class ConnectThread extends Thread { 389*90c8c64dSAndroid Build Coastguard Worker private final BluetoothSocket mmSocket; 390*90c8c64dSAndroid Build Coastguard Worker private final BluetoothDevice mmDevice; 391*90c8c64dSAndroid Build Coastguard Worker private String mSocketType; 392*90c8c64dSAndroid Build Coastguard Worker ConnectThread(BluetoothDevice device, boolean secure)393*90c8c64dSAndroid Build Coastguard Worker public ConnectThread(BluetoothDevice device, boolean secure) { 394*90c8c64dSAndroid Build Coastguard Worker mmDevice = device; 395*90c8c64dSAndroid Build Coastguard Worker BluetoothSocket tmp = null; 396*90c8c64dSAndroid Build Coastguard Worker mSocketType = secure ? "Secure" : "Insecure"; 397*90c8c64dSAndroid Build Coastguard Worker 398*90c8c64dSAndroid Build Coastguard Worker // Get a BluetoothSocket for a connection with the 399*90c8c64dSAndroid Build Coastguard Worker // given BluetoothDevice 400*90c8c64dSAndroid Build Coastguard Worker try { 401*90c8c64dSAndroid Build Coastguard Worker if (secure) { 402*90c8c64dSAndroid Build Coastguard Worker tmp = device.createRfcommSocketToServiceRecord( 403*90c8c64dSAndroid Build Coastguard Worker MY_UUID_SECURE); 404*90c8c64dSAndroid Build Coastguard Worker } else { 405*90c8c64dSAndroid Build Coastguard Worker tmp = device.createInsecureRfcommSocketToServiceRecord( 406*90c8c64dSAndroid Build Coastguard Worker MY_UUID_INSECURE); 407*90c8c64dSAndroid Build Coastguard Worker } 408*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 409*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); 410*90c8c64dSAndroid Build Coastguard Worker } 411*90c8c64dSAndroid Build Coastguard Worker mmSocket = tmp; 412*90c8c64dSAndroid Build Coastguard Worker mState = STATE_CONNECTING; 413*90c8c64dSAndroid Build Coastguard Worker } 414*90c8c64dSAndroid Build Coastguard Worker run()415*90c8c64dSAndroid Build Coastguard Worker public void run() { 416*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); 417*90c8c64dSAndroid Build Coastguard Worker setName("ConnectThread" + mSocketType); 418*90c8c64dSAndroid Build Coastguard Worker 419*90c8c64dSAndroid Build Coastguard Worker // Always cancel discovery because it will slow down a connection 420*90c8c64dSAndroid Build Coastguard Worker mAdapter.cancelDiscovery(); 421*90c8c64dSAndroid Build Coastguard Worker 422*90c8c64dSAndroid Build Coastguard Worker // Make a connection to the BluetoothSocket 423*90c8c64dSAndroid Build Coastguard Worker try { 424*90c8c64dSAndroid Build Coastguard Worker // This is a blocking call and will only return on a 425*90c8c64dSAndroid Build Coastguard Worker // successful connection or an exception 426*90c8c64dSAndroid Build Coastguard Worker mmSocket.connect(); 427*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 428*90c8c64dSAndroid Build Coastguard Worker // Close the socket 429*90c8c64dSAndroid Build Coastguard Worker try { 430*90c8c64dSAndroid Build Coastguard Worker mmSocket.close(); 431*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e2) { 432*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "unable to close() " + mSocketType + 433*90c8c64dSAndroid Build Coastguard Worker " socket during connection failure", e2); 434*90c8c64dSAndroid Build Coastguard Worker } 435*90c8c64dSAndroid Build Coastguard Worker connectionFailed(); 436*90c8c64dSAndroid Build Coastguard Worker return; 437*90c8c64dSAndroid Build Coastguard Worker } 438*90c8c64dSAndroid Build Coastguard Worker 439*90c8c64dSAndroid Build Coastguard Worker // Reset the ConnectThread because we're done 440*90c8c64dSAndroid Build Coastguard Worker synchronized (BluetoothChatService.this) { 441*90c8c64dSAndroid Build Coastguard Worker mConnectThread = null; 442*90c8c64dSAndroid Build Coastguard Worker } 443*90c8c64dSAndroid Build Coastguard Worker 444*90c8c64dSAndroid Build Coastguard Worker // Start the connected thread 445*90c8c64dSAndroid Build Coastguard Worker connected(mmSocket, mmDevice, mSocketType); 446*90c8c64dSAndroid Build Coastguard Worker } 447*90c8c64dSAndroid Build Coastguard Worker cancel()448*90c8c64dSAndroid Build Coastguard Worker public void cancel() { 449*90c8c64dSAndroid Build Coastguard Worker try { 450*90c8c64dSAndroid Build Coastguard Worker mmSocket.close(); 451*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 452*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); 453*90c8c64dSAndroid Build Coastguard Worker } 454*90c8c64dSAndroid Build Coastguard Worker } 455*90c8c64dSAndroid Build Coastguard Worker } 456*90c8c64dSAndroid Build Coastguard Worker 457*90c8c64dSAndroid Build Coastguard Worker /** 458*90c8c64dSAndroid Build Coastguard Worker * This thread runs during a connection with a remote device. 459*90c8c64dSAndroid Build Coastguard Worker * It handles all incoming and outgoing transmissions. 460*90c8c64dSAndroid Build Coastguard Worker */ 461*90c8c64dSAndroid Build Coastguard Worker private class ConnectedThread extends Thread { 462*90c8c64dSAndroid Build Coastguard Worker private final BluetoothSocket mmSocket; 463*90c8c64dSAndroid Build Coastguard Worker private final InputStream mmInStream; 464*90c8c64dSAndroid Build Coastguard Worker private final OutputStream mmOutStream; 465*90c8c64dSAndroid Build Coastguard Worker ConnectedThread(BluetoothSocket socket, String socketType)466*90c8c64dSAndroid Build Coastguard Worker public ConnectedThread(BluetoothSocket socket, String socketType) { 467*90c8c64dSAndroid Build Coastguard Worker Log.d(TAG, "create ConnectedThread: " + socketType); 468*90c8c64dSAndroid Build Coastguard Worker mmSocket = socket; 469*90c8c64dSAndroid Build Coastguard Worker InputStream tmpIn = null; 470*90c8c64dSAndroid Build Coastguard Worker OutputStream tmpOut = null; 471*90c8c64dSAndroid Build Coastguard Worker 472*90c8c64dSAndroid Build Coastguard Worker // Get the BluetoothSocket input and output streams 473*90c8c64dSAndroid Build Coastguard Worker try { 474*90c8c64dSAndroid Build Coastguard Worker tmpIn = socket.getInputStream(); 475*90c8c64dSAndroid Build Coastguard Worker tmpOut = socket.getOutputStream(); 476*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 477*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "temp sockets not created", e); 478*90c8c64dSAndroid Build Coastguard Worker } 479*90c8c64dSAndroid Build Coastguard Worker 480*90c8c64dSAndroid Build Coastguard Worker mmInStream = tmpIn; 481*90c8c64dSAndroid Build Coastguard Worker mmOutStream = tmpOut; 482*90c8c64dSAndroid Build Coastguard Worker mState = STATE_CONNECTED; 483*90c8c64dSAndroid Build Coastguard Worker } 484*90c8c64dSAndroid Build Coastguard Worker run()485*90c8c64dSAndroid Build Coastguard Worker public void run() { 486*90c8c64dSAndroid Build Coastguard Worker Log.i(TAG, "BEGIN mConnectedThread"); 487*90c8c64dSAndroid Build Coastguard Worker byte[] buffer = new byte[1024]; 488*90c8c64dSAndroid Build Coastguard Worker int bytes; 489*90c8c64dSAndroid Build Coastguard Worker 490*90c8c64dSAndroid Build Coastguard Worker // Keep listening to the InputStream while connected 491*90c8c64dSAndroid Build Coastguard Worker while (mState == STATE_CONNECTED) { 492*90c8c64dSAndroid Build Coastguard Worker try { 493*90c8c64dSAndroid Build Coastguard Worker // Read from the InputStream 494*90c8c64dSAndroid Build Coastguard Worker bytes = mmInStream.read(buffer); 495*90c8c64dSAndroid Build Coastguard Worker 496*90c8c64dSAndroid Build Coastguard Worker // Send the obtained bytes to the UI Activity 497*90c8c64dSAndroid Build Coastguard Worker mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer) 498*90c8c64dSAndroid Build Coastguard Worker .sendToTarget(); 499*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 500*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "disconnected", e); 501*90c8c64dSAndroid Build Coastguard Worker connectionLost(); 502*90c8c64dSAndroid Build Coastguard Worker break; 503*90c8c64dSAndroid Build Coastguard Worker } 504*90c8c64dSAndroid Build Coastguard Worker } 505*90c8c64dSAndroid Build Coastguard Worker } 506*90c8c64dSAndroid Build Coastguard Worker 507*90c8c64dSAndroid Build Coastguard Worker /** 508*90c8c64dSAndroid Build Coastguard Worker * Write to the connected OutStream. 509*90c8c64dSAndroid Build Coastguard Worker * 510*90c8c64dSAndroid Build Coastguard Worker * @param buffer The bytes to write 511*90c8c64dSAndroid Build Coastguard Worker */ write(byte[] buffer)512*90c8c64dSAndroid Build Coastguard Worker public void write(byte[] buffer) { 513*90c8c64dSAndroid Build Coastguard Worker try { 514*90c8c64dSAndroid Build Coastguard Worker mmOutStream.write(buffer); 515*90c8c64dSAndroid Build Coastguard Worker 516*90c8c64dSAndroid Build Coastguard Worker // Share the sent message back to the UI Activity 517*90c8c64dSAndroid Build Coastguard Worker mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer) 518*90c8c64dSAndroid Build Coastguard Worker .sendToTarget(); 519*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 520*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Exception during write", e); 521*90c8c64dSAndroid Build Coastguard Worker } 522*90c8c64dSAndroid Build Coastguard Worker } 523*90c8c64dSAndroid Build Coastguard Worker cancel()524*90c8c64dSAndroid Build Coastguard Worker public void cancel() { 525*90c8c64dSAndroid Build Coastguard Worker try { 526*90c8c64dSAndroid Build Coastguard Worker mmSocket.close(); 527*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 528*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "close() of connect socket failed", e); 529*90c8c64dSAndroid Build Coastguard Worker } 530*90c8c64dSAndroid Build Coastguard Worker } 531*90c8c64dSAndroid Build Coastguard Worker } 532*90c8c64dSAndroid Build Coastguard Worker } 533