1 package android.bluetooth; 2 3 import java.lang.Thread; 4 5 import android.os.Message; 6 import android.os.Handler; 7 import android.util.Log; 8 9 /** 10 * Listen's for incoming RFCOMM connection for the headset / handsfree service. 11 * 12 * TODO: Use the new generic BluetoothSocket class instead of this legacy code 13 * 14 * @hide 15 */ 16 public final class BluetoothAudioGateway { 17 private static final String TAG = "BT Audio Gateway"; 18 private static final boolean DBG = false; 19 20 private int mNativeData; classInitNative()21 static { classInitNative(); } 22 23 /* in */ 24 private int mHandsfreeAgRfcommChannel = -1; 25 private int mHeadsetAgRfcommChannel = -1; 26 27 /* out - written by native code */ 28 private String mConnectingHeadsetAddress; 29 private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */ 30 private int mConnectingHeadsetSocketFd; 31 private String mConnectingHandsfreeAddress; 32 private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */ 33 private int mConnectingHandsfreeSocketFd; 34 private int mTimeoutRemainingMs; /* in/out */ 35 36 private final BluetoothAdapter mAdapter; 37 38 public static final int DEFAULT_HF_AG_CHANNEL = 10; 39 public static final int DEFAULT_HS_AG_CHANNEL = 11; 40 BluetoothAudioGateway(BluetoothAdapter adapter)41 public BluetoothAudioGateway(BluetoothAdapter adapter) { 42 this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL); 43 } 44 BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel, int headsetAgRfcommChannel)45 public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel, 46 int headsetAgRfcommChannel) { 47 mAdapter = adapter; 48 mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel; 49 mHeadsetAgRfcommChannel = headsetAgRfcommChannel; 50 initializeNativeDataNative(); 51 } 52 53 private Thread mConnectThead; 54 private volatile boolean mInterrupted; 55 private static final int SELECT_WAIT_TIMEOUT = 1000; 56 57 private Handler mCallback; 58 59 public class IncomingConnectionInfo { 60 public BluetoothAdapter mAdapter; 61 public BluetoothDevice mRemoteDevice; 62 public int mSocketFd; 63 public int mRfcommChan; IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice, int socketFd, int rfcommChan)64 IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice, 65 int socketFd, int rfcommChan) { 66 mAdapter = adapter; 67 mRemoteDevice = remoteDevice; 68 mSocketFd = socketFd; 69 mRfcommChan = rfcommChan; 70 } 71 } 72 73 public static final int MSG_INCOMING_HEADSET_CONNECTION = 100; 74 public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101; 75 start(Handler callback)76 public synchronized boolean start(Handler callback) { 77 78 if (mConnectThead == null) { 79 mCallback = callback; 80 mConnectThead = new Thread(TAG) { 81 public void run() { 82 if (DBG) log("Connect Thread starting"); 83 while (!mInterrupted) { 84 //Log.i(TAG, "waiting for connect"); 85 mConnectingHeadsetRfcommChannel = -1; 86 mConnectingHandsfreeRfcommChannel = -1; 87 if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) { 88 if (mTimeoutRemainingMs > 0) { 89 try { 90 Log.i(TAG, "select thread timed out, but " + 91 mTimeoutRemainingMs + "ms of waiting remain."); 92 Thread.sleep(mTimeoutRemainingMs); 93 } catch (InterruptedException e) { 94 Log.i(TAG, "select thread was interrupted (2), exiting"); 95 mInterrupted = true; 96 } 97 } 98 } 99 else { 100 Log.i(TAG, "connect notification!"); 101 /* A device connected (most likely just one, but 102 it is possible for two separate devices, one 103 a headset and one a handsfree, to connect 104 simultaneously. 105 */ 106 if (mConnectingHeadsetRfcommChannel >= 0) { 107 Log.i(TAG, "Incoming connection from headset " + 108 mConnectingHeadsetAddress + " on channel " + 109 mConnectingHeadsetRfcommChannel); 110 Message msg = Message.obtain(mCallback); 111 msg.what = MSG_INCOMING_HEADSET_CONNECTION; 112 msg.obj = new IncomingConnectionInfo( 113 mAdapter, 114 mAdapter.getRemoteDevice(mConnectingHeadsetAddress), 115 mConnectingHeadsetSocketFd, 116 mConnectingHeadsetRfcommChannel); 117 msg.sendToTarget(); 118 } 119 if (mConnectingHandsfreeRfcommChannel >= 0) { 120 Log.i(TAG, "Incoming connection from handsfree " + 121 mConnectingHandsfreeAddress + " on channel " + 122 mConnectingHandsfreeRfcommChannel); 123 Message msg = Message.obtain(); 124 msg.setTarget(mCallback); 125 msg.what = MSG_INCOMING_HANDSFREE_CONNECTION; 126 msg.obj = new IncomingConnectionInfo( 127 mAdapter, 128 mAdapter.getRemoteDevice(mConnectingHandsfreeAddress), 129 mConnectingHandsfreeSocketFd, 130 mConnectingHandsfreeRfcommChannel); 131 msg.sendToTarget(); 132 } 133 } 134 } 135 if (DBG) log("Connect Thread finished"); 136 } 137 }; 138 139 if (setUpListeningSocketsNative() == false) { 140 Log.e(TAG, "Could not set up listening socket, exiting"); 141 return false; 142 } 143 144 mInterrupted = false; 145 mConnectThead.start(); 146 } 147 148 return true; 149 } 150 stop()151 public synchronized void stop() { 152 if (mConnectThead != null) { 153 if (DBG) log("stopping Connect Thread"); 154 mInterrupted = true; 155 try { 156 mConnectThead.interrupt(); 157 if (DBG) log("waiting for thread to terminate"); 158 mConnectThead.join(); 159 mConnectThead = null; 160 mCallback = null; 161 tearDownListeningSocketsNative(); 162 } catch (InterruptedException e) { 163 Log.w(TAG, "Interrupted waiting for Connect Thread to join"); 164 } 165 } 166 } 167 finalize()168 protected void finalize() throws Throwable { 169 try { 170 cleanupNativeDataNative(); 171 } finally { 172 super.finalize(); 173 } 174 } 175 classInitNative()176 private static native void classInitNative(); initializeNativeDataNative()177 private native void initializeNativeDataNative(); cleanupNativeDataNative()178 private native void cleanupNativeDataNative(); waitForHandsfreeConnectNative(int timeoutMs)179 private native boolean waitForHandsfreeConnectNative(int timeoutMs); setUpListeningSocketsNative()180 private native boolean setUpListeningSocketsNative(); tearDownListeningSocketsNative()181 private native void tearDownListeningSocketsNative(); 182 log(String msg)183 private static void log(String msg) { 184 Log.d(TAG, msg); 185 } 186 } 187