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