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