2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.bluetooth;
19 import java.lang.Thread;
21 import android.os.Message;
22 import android.os.Handler;
23 import android.util.Log;
26 * Listens for incoming RFCOMM connection for the headset / handsfree service.
28 * TODO: Use the new generic BluetoothSocket class instead of this legacy code
32 public final class BluetoothAudioGateway {
33 private static final String TAG = "BT Audio Gateway";
34 private static final boolean DBG = false;
36 private int mNativeData;
37 static { classInitNative(); }
40 private int mHandsfreeAgRfcommChannel = -1;
41 private int mHeadsetAgRfcommChannel = -1;
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 */
52 private final BluetoothAdapter mAdapter;
54 public static final int DEFAULT_HF_AG_CHANNEL = 10;
55 public static final int DEFAULT_HS_AG_CHANNEL = 11;
57 public BluetoothAudioGateway(BluetoothAdapter adapter) {
58 this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
61 public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel,
62 int headsetAgRfcommChannel) {
64 mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel;
65 mHeadsetAgRfcommChannel = headsetAgRfcommChannel;
66 initializeNativeDataNative();
69 private Thread mConnectThead;
70 private volatile boolean mInterrupted;
71 private static final int SELECT_WAIT_TIMEOUT = 1000;
73 private Handler mCallback;
75 public class IncomingConnectionInfo {
76 public BluetoothAdapter mAdapter;
77 public BluetoothDevice mRemoteDevice;
79 public int mRfcommChan;
80 IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice,
81 int socketFd, int rfcommChan) {
83 mRemoteDevice = remoteDevice;
85 mRfcommChan = rfcommChan;
89 public static final int MSG_INCOMING_HEADSET_CONNECTION = 100;
90 public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101;
92 public synchronized boolean start(Handler callback) {
94 if (mConnectThead == null) {
96 mConnectThead = new Thread(TAG) {
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) {
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");
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
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(
130 mAdapter.getRemoteDevice(mConnectingHeadsetAddress),
131 mConnectingHeadsetSocketFd,
132 mConnectingHeadsetRfcommChannel);
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(
144 mAdapter.getRemoteDevice(mConnectingHandsfreeAddress),
145 mConnectingHandsfreeSocketFd,
146 mConnectingHandsfreeRfcommChannel);
151 if (DBG) log("Connect Thread finished");
155 if (setUpListeningSocketsNative() == false) {
156 Log.e(TAG, "Could not set up listening socket, exiting");
160 mInterrupted = false;
161 mConnectThead.start();
167 public synchronized void stop() {
168 if (mConnectThead != null) {
169 if (DBG) log("stopping Connect Thread");
172 mConnectThead.interrupt();
173 if (DBG) log("waiting for thread to terminate");
174 mConnectThead.join();
175 mConnectThead = null;
177 tearDownListeningSocketsNative();
178 } catch (InterruptedException e) {
179 Log.w(TAG, "Interrupted waiting for Connect Thread to join");
184 protected void finalize() throws Throwable {
186 cleanupNativeDataNative();
192 private static native void classInitNative();
193 private native void initializeNativeDataNative();
194 private native void cleanupNativeDataNative();
195 private native boolean waitForHandsfreeConnectNative(int timeoutMs);
196 private native boolean setUpListeningSocketsNative();
197 private native void tearDownListeningSocketsNative();
199 private static void log(String msg) {