Methods Summary |
---|
private native void | cleanupNative()
|
public synchronized int | connectSink(java.lang.String address)
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (DBG) log("connectSink(" + address + ")");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
return BluetoothError.ERROR;
}
// ignore if there are any active sinks
if (lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
BluetoothA2dp.STATE_CONNECTED,
BluetoothA2dp.STATE_PLAYING,
BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
return BluetoothError.ERROR;
}
String path = lookupPath(address);
if (path == null) {
path = createHeadsetNative(address);
if (DBG) log("new bluez sink: " + address + " (" + path + ")");
}
if (path == null) {
return BluetoothError.ERROR;
}
SinkState sink = mAudioDevices.get(path);
int state = BluetoothA2dp.STATE_DISCONNECTED;
if (sink != null) {
state = sink.state;
}
switch (state) {
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
case BluetoothA2dp.STATE_DISCONNECTING:
return BluetoothError.ERROR;
case BluetoothA2dp.STATE_CONNECTING:
return BluetoothError.SUCCESS;
}
// State is DISCONNECTED
if (!connectSinkNative(path)) {
return BluetoothError.ERROR;
}
updateState(path, BluetoothA2dp.STATE_CONNECTING);
return BluetoothError.SUCCESS;
|
private native synchronized boolean | connectSinkNative(java.lang.String path)
|
private native synchronized java.lang.String | createHeadsetNative(java.lang.String address)
|
public synchronized int | disconnectSink(java.lang.String address)
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (DBG) log("disconnectSink(" + address + ")");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
return BluetoothError.ERROR;
}
String path = lookupPath(address);
if (path == null) {
return BluetoothError.ERROR;
}
switch (mAudioDevices.get(path).state) {
case BluetoothA2dp.STATE_DISCONNECTED:
return BluetoothError.ERROR;
case BluetoothA2dp.STATE_DISCONNECTING:
return BluetoothError.SUCCESS;
}
// State is CONNECTING or CONNECTED or PLAYING
if (!disconnectSinkNative(path)) {
return BluetoothError.ERROR;
} else {
updateState(path, BluetoothA2dp.STATE_DISCONNECTING);
return BluetoothError.SUCCESS;
}
|
private native synchronized boolean | disconnectSinkNative(java.lang.String path)
|
protected synchronized void | dump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)
if (mAudioDevices == null) return;
pw.println("Cached audio devices:");
for (String path : mAudioDevices.keySet()) {
SinkState sink = mAudioDevices.get(path);
pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state));
}
|
protected void | finalize()
try {
cleanupNative();
} finally {
super.finalize();
}
|
private native synchronized java.lang.String | getAddressNative(java.lang.String path)
|
public synchronized int | getSinkPriority(java.lang.String address)
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothA2dpSinkPriorityKey(address),
BluetoothA2dp.PRIORITY_OFF);
|
public synchronized int | getSinkState(java.lang.String address)
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
if (mAudioDevices == null) {
return BluetoothA2dp.STATE_DISCONNECTED;
}
for (SinkState sink : mAudioDevices.values()) {
if (address.equals(sink.address)) {
return sink.state;
}
}
return BluetoothA2dp.STATE_DISCONNECTED;
|
private synchronized void | handleDeferredDisconnect(java.lang.String path)
if (mPendingDisconnects.contains(path)) {
mPendingDisconnects.remove(path);
if (mSinkCount == 1) {
mAudioManager.setBluetoothA2dpOn(false);
}
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
|
private native boolean | initNative()
|
private native synchronized boolean | isSinkConnectedNative(java.lang.String path)
|
public synchronized java.util.List | listConnectedSinks()
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return lookupSinksMatchingStates(new int[] {BluetoothA2dp.STATE_CONNECTED,
BluetoothA2dp.STATE_PLAYING});
|
private native synchronized java.lang.String[] | listHeadsetsNative()
|
private static void | log(java.lang.String msg)
Log.d(TAG, msg);
|
private final synchronized java.lang.String | lookupAddress(java.lang.String path)
if (mAudioDevices == null) return null;
SinkState sink = mAudioDevices.get(path);
if (sink == null) {
Log.w(TAG, "lookupAddress() called for unknown device " + path);
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
String address = mAudioDevices.get(path).address;
if (address == null) Log.e(TAG, "Can't find address for " + path);
return address;
|
private final synchronized java.lang.String | lookupPath(java.lang.String address)
if (mAudioDevices == null) return null;
for (String path : mAudioDevices.keySet()) {
if (address.equals(mAudioDevices.get(path).address)) {
return path;
}
}
return null;
|
private synchronized java.util.List | lookupSinksMatchingStates(int[] states)
List<String> sinks = new ArrayList<String>();
if (mAudioDevices == null) {
return sinks;
}
for (SinkState sink : mAudioDevices.values()) {
for (int state : states) {
if (sink.state == state) {
sinks.add(sink.address);
break;
}
}
}
return sinks;
|
private synchronized void | onBluetoothDisable()
if (mAudioDevices != null) {
// copy to allow modification during iteration
String[] paths = new String[mAudioDevices.size()];
paths = mAudioDevices.keySet().toArray(paths);
for (String path : paths) {
switch (mAudioDevices.get(path).state) {
case BluetoothA2dp.STATE_CONNECTING:
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
disconnectSinkNative(path);
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
break;
case BluetoothA2dp.STATE_DISCONNECTING:
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
break;
}
}
mAudioDevices = null;
}
mAudioManager.setBluetoothA2dpOn(false);
mAudioManager.setParameter(BLUETOOTH_ENABLED, "false");
|
private synchronized void | onBluetoothEnable()
mAudioDevices = new HashMap<String, SinkState>();
String[] paths = (String[])listHeadsetsNative();
if (paths != null) {
for (String path : paths) {
mAudioDevices.put(path, new SinkState(getAddressNative(path),
isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED :
BluetoothA2dp.STATE_DISCONNECTED));
}
}
mAudioManager.setParameter(BLUETOOTH_ENABLED, "true");
|
private synchronized void | onHeadsetCreated(java.lang.String path)
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
|
private synchronized void | onHeadsetRemoved(java.lang.String path)
if (mAudioDevices == null) return;
mAudioDevices.remove(path);
|
private synchronized void | onSinkConnected(java.lang.String path)
// if we are reconnected, do not process previous disconnect event.
mPendingDisconnects.remove(path);
if (mAudioDevices == null) return;
// bluez 3.36 quietly disconnects the previous sink when a new sink
// is connected, so we need to mark all previously connected sinks as
// disconnected
// copy to allow modification during iteration
String[] paths = new String[mAudioDevices.size()];
paths = mAudioDevices.keySet().toArray(paths);
for (String oldPath : paths) {
if (path.equals(oldPath)) {
continue;
}
int state = mAudioDevices.get(oldPath).state;
if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) {
updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
}
updateState(path, BluetoothA2dp.STATE_CONNECTING);
mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path));
mAudioManager.setBluetoothA2dpOn(true);
updateState(path, BluetoothA2dp.STATE_CONNECTED);
|
private synchronized void | onSinkDisconnected(java.lang.String path)
// This is to work around a problem in bluez that results
// sink disconnect events being sent, immediately followed by a reconnect.
// To avoid unnecessary audio routing changes, we defer handling
// sink disconnects until after a short delay.
mPendingDisconnects.add(path);
Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path);
mHandler.sendMessageDelayed(msg, 2000);
|
private synchronized void | onSinkPlaying(java.lang.String path)
updateState(path, BluetoothA2dp.STATE_PLAYING);
|
private synchronized void | onSinkStopped(java.lang.String path)
updateState(path, BluetoothA2dp.STATE_CONNECTED);
|
private native synchronized boolean | removeHeadsetNative(java.lang.String path)
|
public synchronized int | setSinkPriority(java.lang.String address, int priority)
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
return BluetoothError.ERROR;
}
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothA2dpSinkPriorityKey(address), priority) ?
BluetoothError.SUCCESS : BluetoothError.ERROR;
|
private synchronized void | updateState(java.lang.String path, int state)
if (mAudioDevices == null) return;
SinkState s = mAudioDevices.get(path);
int prevState;
String address;
if (s == null) {
address = getAddressNative(path);
mAudioDevices.put(path, new SinkState(address, state));
prevState = BluetoothA2dp.STATE_DISCONNECTED;
} else {
address = lookupAddress(path);
prevState = s.state;
s.state = state;
}
if (state != prevState) {
if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state);
// keep track of the number of active sinks
if (prevState == BluetoothA2dp.STATE_DISCONNECTED) {
mSinkCount++;
} else if (state == BluetoothA2dp.STATE_DISCONNECTED) {
mSinkCount--;
}
Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothA2dp.SINK_STATE, state);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if ((prevState == BluetoothA2dp.STATE_CONNECTED ||
prevState == BluetoothA2dp.STATE_PLAYING) &&
(state != BluetoothA2dp.STATE_CONNECTING &&
state != BluetoothA2dp.STATE_CONNECTED &&
state != BluetoothA2dp.STATE_PLAYING)) {
// disconnected
intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(intent);
}
}
|