blob: 9351393728599b1931ebbe4adf1229417a0e30c1 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import java.lang.Thread;
import android.os.Message;
import android.os.Handler;
import android.util.Log;
/**
* Listens for incoming RFCOMM connection for the headset / handsfree service.
*
* TODO: Use the new generic BluetoothSocket class instead of this legacy code
*
* @hide
*/
public final class BluetoothAudioGateway {
private static final String TAG = "BT Audio Gateway";
private static final boolean DBG = false;
private int mNativeData;
static { classInitNative(); }
/* in */
private int mHandsfreeAgRfcommChannel = -1;
private int mHeadsetAgRfcommChannel = -1;
/* out - written by native code */
private String mConnectingHeadsetAddress;
private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */
private int mConnectingHeadsetSocketFd;
private String mConnectingHandsfreeAddress;
private int mConnectingHandsfreeRfcommChannel; /* -1 when not connected */
private int mConnectingHandsfreeSocketFd;
private int mTimeoutRemainingMs; /* in/out */
private final BluetoothAdapter mAdapter;
public static final int DEFAULT_HF_AG_CHANNEL = 10;
public static final int DEFAULT_HS_AG_CHANNEL = 11;
public BluetoothAudioGateway(BluetoothAdapter adapter) {
this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
}
public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel,
int headsetAgRfcommChannel) {
mAdapter = adapter;
mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel;
mHeadsetAgRfcommChannel = headsetAgRfcommChannel;
initializeNativeDataNative();
}
private Thread mConnectThead;
private volatile boolean mInterrupted;
private static final int SELECT_WAIT_TIMEOUT = 1000;
private Handler mCallback;
public class IncomingConnectionInfo {
public BluetoothAdapter mAdapter;
public BluetoothDevice mRemoteDevice;
public int mSocketFd;
public int mRfcommChan;
IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice,
int socketFd, int rfcommChan) {
mAdapter = adapter;
mRemoteDevice = remoteDevice;
mSocketFd = socketFd;
mRfcommChan = rfcommChan;
}
}
public static final int MSG_INCOMING_HEADSET_CONNECTION = 100;
public static final int MSG_INCOMING_HANDSFREE_CONNECTION = 101;
public synchronized boolean start(Handler callback) {
if (mConnectThead == null) {
mCallback = callback;
mConnectThead = new Thread(TAG) {
public void run() {
if (DBG) log("Connect Thread starting");
while (!mInterrupted) {
//Log.i(TAG, "waiting for connect");
mConnectingHeadsetRfcommChannel = -1;
mConnectingHandsfreeRfcommChannel = -1;
if (waitForHandsfreeConnectNative(SELECT_WAIT_TIMEOUT) == false) {
if (mTimeoutRemainingMs > 0) {
try {
Log.i(TAG, "select thread timed out, but " +
mTimeoutRemainingMs + "ms of waiting remain.");
Thread.sleep(mTimeoutRemainingMs);
} catch (InterruptedException e) {
Log.i(TAG, "select thread was interrupted (2), exiting");
mInterrupted = true;
}
}
}
else {
Log.i(TAG, "connect notification!");
/* A device connected (most likely just one, but
it is possible for two separate devices, one
a headset and one a handsfree, to connect
simultaneously.
*/
if (mConnectingHeadsetRfcommChannel >= 0) {
Log.i(TAG, "Incoming connection from headset " +
mConnectingHeadsetAddress + " on channel " +
mConnectingHeadsetRfcommChannel);
Message msg = Message.obtain(mCallback);
msg.what = MSG_INCOMING_HEADSET_CONNECTION;
msg.obj = new IncomingConnectionInfo(
mAdapter,
mAdapter.getRemoteDevice(mConnectingHeadsetAddress),
mConnectingHeadsetSocketFd,
mConnectingHeadsetRfcommChannel);
msg.sendToTarget();
}
if (mConnectingHandsfreeRfcommChannel >= 0) {
Log.i(TAG, "Incoming connection from handsfree " +
mConnectingHandsfreeAddress + " on channel " +
mConnectingHandsfreeRfcommChannel);
Message msg = Message.obtain();
msg.setTarget(mCallback);
msg.what = MSG_INCOMING_HANDSFREE_CONNECTION;
msg.obj = new IncomingConnectionInfo(
mAdapter,
mAdapter.getRemoteDevice(mConnectingHandsfreeAddress),
mConnectingHandsfreeSocketFd,
mConnectingHandsfreeRfcommChannel);
msg.sendToTarget();
}
}
}
if (DBG) log("Connect Thread finished");
}
};
if (setUpListeningSocketsNative() == false) {
Log.e(TAG, "Could not set up listening socket, exiting");
return false;
}
mInterrupted = false;
mConnectThead.start();
}
return true;
}
public synchronized void stop() {
if (mConnectThead != null) {
if (DBG) log("stopping Connect Thread");
mInterrupted = true;
try {
mConnectThead.interrupt();
if (DBG) log("waiting for thread to terminate");
mConnectThead.join();
mConnectThead = null;
mCallback = null;
tearDownListeningSocketsNative();
} catch (InterruptedException e) {
Log.w(TAG, "Interrupted waiting for Connect Thread to join");
}
}
}
protected void finalize() throws Throwable {
try {
cleanupNativeDataNative();
} finally {
super.finalize();
}
}
private static native void classInitNative();
private native void initializeNativeDataNative();
private native void cleanupNativeDataNative();
private native boolean waitForHandsfreeConnectNative(int timeoutMs);
private native boolean setUpListeningSocketsNative();
private native void tearDownListeningSocketsNative();
private static void log(String msg) {
Log.d(TAG, msg);
}
}