• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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