summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java107
-rw-r--r--core/java/android/bluetooth/BluetoothProfileState.java20
-rw-r--r--core/java/android/bluetooth/IBluetoothA2dp.aidl24
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadset.aidl25
-rw-r--r--core/java/android/server/BluetoothA2dpService.java161
-rw-r--r--core/java/android/server/BluetoothEventLoop.java33
-rw-r--r--core/java/android/server/BluetoothService.java79
7 files changed, 272 insertions, 177 deletions
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 1fd71518dee3..e460839deff0 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Message;
+import android.bluetooth.BluetoothAdapter;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.util.Log;
@@ -28,6 +29,8 @@ import android.util.Log;
import com.android.internal.util.HierarchicalState;
import com.android.internal.util.HierarchicalStateMachine;
+import java.util.Set;
+
/**
* This class is the Profile connection state machine associated with a remote
* device. When the device bonds an instance of this class is created.
@@ -91,12 +94,11 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
private BluetoothService mService;
private BluetoothA2dpService mA2dpService;
private BluetoothHeadset mHeadsetService;
- private boolean mHeadsetServiceConnected;
private BluetoothDevice mDevice;
- private int mHeadsetState;
- private int mA2dpState;
- private int mHidState;
+ private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
+ private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED;
+ private int mHidState = BluetoothProfile.STATE_DISCONNECTED;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -105,32 +107,29 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (!device.equals(mDevice)) return;
- if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
- int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
- int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0);
- int initiator = intent.getIntExtra(
- BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
- BluetoothHeadset.LOCAL_DISCONNECT);
- mHeadsetState = newState;
- if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
- initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
- sendMessage(DISCONNECT_HFP_INCOMING);
+ if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ mA2dpState = newState;
+ if (oldState == BluetoothA2dp.STATE_CONNECTED &&
+ newState == BluetoothA2dp.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_A2DP_INCOMING);
}
- if (newState == BluetoothHeadset.STATE_CONNECTED ||
- newState == BluetoothHeadset.STATE_DISCONNECTED) {
+ if (newState == BluetoothProfile.STATE_CONNECTED ||
+ newState == BluetoothProfile.STATE_DISCONNECTED) {
sendMessage(TRANSITION_TO_STABLE);
}
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
- int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
- mA2dpState = newState;
- if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
- oldState == BluetoothA2dp.STATE_PLAYING) &&
- newState == BluetoothA2dp.STATE_DISCONNECTED) {
- sendMessage(DISCONNECT_A2DP_INCOMING);
+ } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+
+ mHeadsetState = newState;
+ if (oldState == BluetoothHeadset.STATE_CONNECTED &&
+ newState == BluetoothHeadset.STATE_DISCONNECTED) {
+ sendMessage(DISCONNECT_HFP_INCOMING);
}
- if (newState == BluetoothA2dp.STATE_CONNECTED ||
- newState == BluetoothA2dp.STATE_DISCONNECTED) {
+ if (newState == BluetoothProfile.STATE_CONNECTED ||
+ newState == BluetoothProfile.STATE_DISCONNECTED) {
sendMessage(TRANSITION_TO_STABLE);
}
} else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
@@ -192,31 +191,31 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
IntentFilter filter = new IntentFilter();
// Fine-grained state broadcasts
- filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, filter);
- HeadsetServiceListener l = new HeadsetServiceListener();
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.HEADSET);
}
- private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
- public HeadsetServiceListener() {
- mHeadsetService = new BluetoothHeadset(mContext, this);
- }
- public void onServiceConnected() {
+ private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
synchronized(BluetoothDeviceProfileState.this) {
- mHeadsetServiceConnected = true;
+ mHeadsetService = (BluetoothHeadset) proxy;
}
}
- public void onServiceDisconnected() {
+ public void onServiceDisconnected(int profile) {
synchronized(BluetoothDeviceProfileState.this) {
- mHeadsetServiceConnected = false;
+ mHeadsetService = null;
}
}
- }
+ };
private class BondedDevice extends HierarchicalState {
@Override
@@ -276,19 +275,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
if (isPhoneDocked(mDevice)) {
// Don't auto connect to docks.
break;
- } else if (!mHeadsetServiceConnected) {
+ } else if (mHeadsetService == null) {
deferMessage(message);
} else {
if (mHeadsetService.getPriority(mDevice) ==
BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
- !mHeadsetService.isConnected(mDevice)) {
- mHeadsetService.connectHeadset(mDevice);
+ mHeadsetService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING}).size() == 0) {
+ mHeadsetService.connect(mDevice);
}
if (mA2dpService != null &&
- mA2dpService.getSinkPriority(mDevice) ==
+ mA2dpService.getPriority(mDevice) ==
BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
- mA2dpService.getConnectedSinks().length == 0) {
- mA2dpService.connectSink(mDevice);
+ mA2dpService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothA2dp.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING}).length == 0) {
+ mA2dpService.connect(mDevice);
}
if (mService.getInputDevicePriority(mDevice) ==
BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
@@ -805,7 +810,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
synchronized void cancelCommand(int command) {
if (command == CONNECT_HFP_OUTGOING ) {
// Cancel the outgoing thread.
- if (mHeadsetServiceConnected) {
+ if (mHeadsetService != null) {
mHeadsetService.cancelConnectThread();
}
// HeadsetService is down. Phone process most likely crashed.
@@ -823,12 +828,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
log("Processing command:" + command);
switch(command) {
case CONNECT_HFP_OUTGOING:
- if (mHeadsetService != null) {
+ if (mHeadsetService == null) {
+ deferHeadsetMessage(command);
+ } else {
return mHeadsetService.connectHeadsetInternal(mDevice);
}
break;
case CONNECT_HFP_INCOMING:
- if (!mHeadsetServiceConnected) {
+ if (mHeadsetService == null) {
deferHeadsetMessage(command);
} else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
return mHeadsetService.acceptIncomingConnect(mDevice);
@@ -849,7 +856,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case CONNECT_HID_INCOMING:
return true;
case DISCONNECT_HFP_OUTGOING:
- if (!mHeadsetServiceConnected) {
+ if (mHeadsetService == null) {
deferHeadsetMessage(command);
} else {
if (mHeadsetService.getPriority(mDevice) ==
@@ -867,9 +874,9 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
return true;
case DISCONNECT_A2DP_OUTGOING:
if (mA2dpService != null) {
- if (mA2dpService.getSinkPriority(mDevice) ==
+ if (mA2dpService.getPriority(mDevice) ==
BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
- mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
+ mA2dpService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
}
return mA2dpService.disconnectSinkInternal(mDevice);
}
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index ad70d0de0ef4..7f42baf78e36 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -59,16 +59,16 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
- int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
- if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
- newState == BluetoothHeadset.STATE_DISCONNECTED)) {
+ if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED ||
+ newState == BluetoothProfile.STATE_DISCONNECTED)) {
sendMessage(TRANSITION_TO_STABLE);
}
- } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
- int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
- if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
- newState == BluetoothA2dp.STATE_DISCONNECTED)) {
+ } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED ||
+ newState == BluetoothProfile.STATE_DISCONNECTED)) {
sendMessage(TRANSITION_TO_STABLE);
}
} else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
@@ -89,8 +89,8 @@ public class BluetoothProfileState extends HierarchicalStateMachine {
setInitialState(mStableState);
IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
- filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 40f10583b637..c5044c23c7ee 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -19,21 +19,25 @@ package android.bluetooth;
import android.bluetooth.BluetoothDevice;
/**
- * System private API for Bluetooth A2DP service
+ * APIs for Bluetooth A2DP service
*
- * {@hide}
+ * @hide
*/
interface IBluetoothA2dp {
- boolean connectSink(in BluetoothDevice device);
- boolean disconnectSink(in BluetoothDevice device);
+ // Public API
+ boolean connect(in BluetoothDevice device);
+ boolean disconnect(in BluetoothDevice device);
+ // change to Set<> once AIDL supports
+ BluetoothDevice[] getConnectedDevices();
+ BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states);
+ int getConnectionState(in BluetoothDevice device);
+ boolean setPriority(in BluetoothDevice device, int priority);
+ int getPriority(in BluetoothDevice device);
+ boolean isA2dpPlaying(in BluetoothDevice device);
+
+ // Internal APIs
boolean suspendSink(in BluetoothDevice device);
boolean resumeSink(in BluetoothDevice device);
- BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports
- BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports
- int getSinkState(in BluetoothDevice device);
- boolean setSinkPriority(in BluetoothDevice device, int priority);
- int getSinkPriority(in BluetoothDevice device);
-
boolean connectSinkInternal(in BluetoothDevice device);
boolean disconnectSinkInternal(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index d96f0ca0de8d..8bcf10335f6d 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -19,25 +19,32 @@ package android.bluetooth;
import android.bluetooth.BluetoothDevice;
/**
- * System private API for Bluetooth Headset service
+ * API for Bluetooth Headset service
*
* {@hide}
*/
interface IBluetoothHeadset {
- int getState(in BluetoothDevice device);
- BluetoothDevice getCurrentHeadset();
- boolean connectHeadset(in BluetoothDevice device);
- void disconnectHeadset(in BluetoothDevice device);
- boolean isConnected(in BluetoothDevice device);
- boolean startVoiceRecognition();
- boolean stopVoiceRecognition();
+ // Public API
+ boolean connect(in BluetoothDevice device);
+ boolean disconnect(in BluetoothDevice device);
+ // Change to Set<> when AIDL supports
+ BluetoothDevice[] getConnectedDevices();
+ BluetoothDevice[] getDevicesMatchingConnectionStates(in int[] states);
+ int getConnectionState(in BluetoothDevice device);
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
- int getBatteryUsageHint();
+ boolean startVoiceRecognition(in BluetoothDevice device);
+ boolean stopVoiceRecognition(in BluetoothDevice device);
+ boolean isAudioConnected(in BluetoothDevice device);
+ // APIs that can be made public in future
+ int getBatteryUsageHint(in BluetoothDevice device);
+
+ // Internal functions, not be made public
boolean createIncomingConnect(in BluetoothDevice device);
boolean acceptIncomingConnect(in BluetoothDevice device);
boolean cancelConnectThread();
boolean connectHeadsetInternal(in BluetoothDevice device);
boolean disconnectHeadsetInternal(in BluetoothDevice device);
+ boolean setAudioState(in BluetoothDevice device, int state);
}
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index a52a221e01aa..946c266bb868 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -25,6 +25,7 @@ package android.server;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothA2dp;
import android.content.BroadcastReceiver;
@@ -32,17 +33,15 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+
public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String TAG = "BluetoothA2dpService";
@@ -57,11 +56,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String PROPERTY_STATE = "State";
- private static final String SINK_STATE_DISCONNECTED = "disconnected";
- private static final String SINK_STATE_CONNECTING = "connecting";
- private static final String SINK_STATE_CONNECTED = "connected";
- private static final String SINK_STATE_PLAYING = "playing";
-
private static int mSinkCount;
private final Context mContext;
@@ -72,6 +66,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private final BluetoothAdapter mAdapter;
private int mTargetA2dpState;
private boolean mAdjustedPriority = false;
+ private BluetoothDevice mPlayingA2dpDevice;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -95,12 +90,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
BluetoothDevice.ERROR);
switch(bondState) {
case BluetoothDevice.BOND_BONDED:
- if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
- setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+ if (getPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
+ setPriority(device, BluetoothA2dp.PRIORITY_ON);
}
break;
case BluetoothDevice.BOND_NONE:
- setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
+ setPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
break;
}
} else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
@@ -113,7 +108,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
} else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
if (streamType == AudioManager.STREAM_MUSIC) {
- BluetoothDevice sinks[] = getConnectedSinks();
+ BluetoothDevice sinks[] = getConnectedDevices();
+
if (sinks.length != 0 && isPhoneDocked(sinks[0])) {
String address = sinks[0].getAddress();
int newVolLevel =
@@ -254,7 +250,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
BluetoothDevice[] devices = new BluetoothDevice[mAudioDevices.size()];
devices = mAudioDevices.keySet().toArray(devices);
for (BluetoothDevice device : devices) {
- int state = getSinkState(device);
+ int state = getConnectionState(device);
switch (state) {
case BluetoothA2dp.STATE_CONNECTING:
case BluetoothA2dp.STATE_CONNECTED:
@@ -277,7 +273,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) {
if (!mBluetoothService.isEnabled() || !isSinkDevice(device) ||
- getSinkPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
+ getPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
return false;
}
@@ -292,12 +288,26 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return true;
}
- public synchronized boolean connectSink(BluetoothDevice device) {
+ public synchronized boolean isA2dpPlaying(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (DBG) log("isA2dpPlaying(" + device + ")");
+ if (device.equals(mPlayingA2dpDevice)) return true;
+ return false;
+ }
+
+ public synchronized boolean connect(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (DBG) log("connectSink(" + device + ")");
if (!isConnectSinkFeasible(device)) return false;
+ for (BluetoothDevice sinkDevice : mAudioDevices.keySet()) {
+ if (getConnectionState(sinkDevice) != BluetoothProfile.STATE_DISCONNECTED) {
+ disconnect(sinkDevice);
+ }
+ }
+
return mBluetoothService.connectSink(device.getAddress());
}
@@ -307,17 +317,15 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
int state = mAudioDevices.get(device);
// ignore if there are any active sinks
- if (lookupSinksMatchingStates(new int[] {
+ if (getDevicesMatchingConnectionStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
BluetoothA2dp.STATE_CONNECTED,
- BluetoothA2dp.STATE_PLAYING,
- BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
+ BluetoothA2dp.STATE_DISCONNECTING}).length != 0) {
return false;
}
switch (state) {
case BluetoothA2dp.STATE_CONNECTED:
- case BluetoothA2dp.STATE_PLAYING:
case BluetoothA2dp.STATE_DISCONNECTING:
return false;
case BluetoothA2dp.STATE_CONNECTING:
@@ -343,17 +351,16 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return false;
}
- int state = getSinkState(device);
+ int state = getConnectionState(device);
switch (state) {
case BluetoothA2dp.STATE_DISCONNECTED:
- return false;
case BluetoothA2dp.STATE_DISCONNECTING:
- return true;
+ return false;
}
return true;
}
- public synchronized boolean disconnectSink(BluetoothDevice device) {
+ public synchronized boolean disconnect(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (DBG) log("disconnectSink(" + device + ")");
@@ -362,7 +369,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
}
public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
- int state = getSinkState(device);
+ int state = getConnectionState(device);
String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
// State is CONNECTING or CONNECTED or PLAYING
@@ -408,44 +415,49 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return checkSinkSuspendState(state.intValue());
}
- public synchronized BluetoothDevice[] getConnectedSinks() {
+ public synchronized int getConnectionState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
- new int[] {BluetoothA2dp.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING});
- return sinks.toArray(new BluetoothDevice[sinks.size()]);
+ Integer state = mAudioDevices.get(device);
+ if (state == null)
+ return BluetoothA2dp.STATE_DISCONNECTED;
+ return state;
}
- public synchronized BluetoothDevice[] getNonDisconnectedSinks() {
+ public synchronized BluetoothDevice[] getConnectedDevices() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
- new int[] {BluetoothA2dp.STATE_CONNECTED,
- BluetoothA2dp.STATE_PLAYING,
- BluetoothA2dp.STATE_CONNECTING,
- BluetoothA2dp.STATE_DISCONNECTING});
- return sinks.toArray(new BluetoothDevice[sinks.size()]);
+ BluetoothDevice[] sinks = getDevicesMatchingConnectionStates(
+ new int[] {BluetoothA2dp.STATE_CONNECTED});
+ return sinks;
}
- public synchronized int getSinkState(BluetoothDevice device) {
+ public synchronized BluetoothDevice[] getDevicesMatchingConnectionStates(int[] states) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Integer state = mAudioDevices.get(device);
- if (state == null)
- return BluetoothA2dp.STATE_DISCONNECTED;
- return state;
+ ArrayList<BluetoothDevice> sinks = new ArrayList();
+ if (mAudioDevices.isEmpty()) {
+ return sinks.toArray(new BluetoothDevice[sinks.size()]);
+ }
+ for (BluetoothDevice device: mAudioDevices.keySet()) {
+ int sinkState = getConnectionState(device);
+ for (int state : states) {
+ if (state == sinkState) {
+ sinks.add(device);
+ break;
+ }
+ }
+ }
+ return sinks.toArray(new BluetoothDevice[sinks.size()]);
}
- public synchronized int getSinkPriority(BluetoothDevice device) {
+ public synchronized int getPriority(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
BluetoothA2dp.PRIORITY_UNDEFINED);
}
- public synchronized boolean setSinkPriority(BluetoothDevice device, int priority) {
+ public synchronized boolean setPriority(BluetoothDevice device, int priority) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- return false;
- }
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
}
@@ -471,8 +483,17 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
// We have authorized it and bluez state has changed.
addAudioSink(device);
} else {
- int prevState = mAudioDevices.get(device);
- handleSinkStateChange(device, prevState, state);
+ if (state == BluetoothA2dp.STATE_PLAYING && mPlayingA2dpDevice == null) {
+ mPlayingA2dpDevice = device;
+ handleSinkPlayingStateChange(device, state, BluetoothA2dp.STATE_NOT_PLAYING);
+ } else if (state == BluetoothA2dp.STATE_CONNECTED && mPlayingA2dpDevice != null) {
+ mPlayingA2dpDevice = null;
+ handleSinkPlayingStateChange(device, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ } else {
+ int prevState = mAudioDevices.get(device);
+ handleSinkStateChange(device, prevState, state);
+ }
}
}
}
@@ -484,18 +505,19 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
mSinkCount--;
} else if (state == BluetoothA2dp.STATE_CONNECTED) {
mSinkCount ++;
+ mPlayingA2dpDevice = null;
}
mAudioDevices.put(device, state);
checkSinkSuspendState(state);
mTargetA2dpState = -1;
- if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
+ if (getPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
state == BluetoothA2dp.STATE_CONNECTING ||
state == BluetoothA2dp.STATE_CONNECTED) {
// We have connected or attempting to connect.
// Bump priority
- setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
+ setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
}
if (state == BluetoothA2dp.STATE_CONNECTED) {
@@ -504,45 +526,38 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
adjustOtherSinkPriorities(device);
}
- Intent intent = new Intent(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- intent.putExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, prevState);
- intent.putExtra(BluetoothA2dp.EXTRA_SINK_STATE, state);
+ intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
}
}
+ private void handleSinkPlayingStateChange(BluetoothDevice device, int state, int prevState) {
+ Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+ if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
+ }
+
private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
if (!mAdjustedPriority) {
for (BluetoothDevice device : mAdapter.getBondedDevices()) {
- if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+ if (getPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
!device.equals(connectedDevice)) {
- setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+ setPriority(device, BluetoothA2dp.PRIORITY_ON);
}
}
mAdjustedPriority = true;
}
}
- private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) {
- Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>();
- if (mAudioDevices.isEmpty()) {
- return sinks;
- }
- for (BluetoothDevice device: mAudioDevices.keySet()) {
- int sinkState = getSinkState(device);
- for (int state : states) {
- if (state == sinkState) {
- sinks.add(device);
- break;
- }
- }
- }
- return sinks;
- }
-
private boolean checkSinkSuspendState(int state) {
boolean result = true;
@@ -568,7 +583,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
if (address == null) return;
BluetoothDevice device = mAdapter.getRemoteDevice(address);
- int state = getSinkState(device);
+ int state = getConnectionState(device);
handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
}
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index ab79aaf645e8..05cbeffeb821 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
@@ -54,6 +55,7 @@ class BluetoothEventLoop {
private final HashMap<String, Integer> mPasskeyAgentRequestData;
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
+ private BluetoothA2dp mA2dp;
private final Context mContext;
// The WakeLock is used for bringing up the LCD during a pairing request
// from remote device when Android is in Suspend state.
@@ -118,8 +120,21 @@ class BluetoothEventLoop {
| PowerManager.ON_AFTER_RELEASE, TAG);
mWakeLock.setReferenceCounted(false);
initializeNativeDataNative();
+
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
}
+ private BluetoothProfile.ServiceListener mProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mA2dp = (BluetoothA2dp) proxy;
+ }
+ public void onServiceDisconnected(int profile) {
+ mA2dp = null;
+ }
+ };
+
+
protected void finalize() throws Throwable {
try {
cleanupNativeDataNative();
@@ -574,12 +589,11 @@ class BluetoothEventLoop {
// Bluez sends the UUID of the local service being accessed, _not_ the
// remote service
- if ((BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
+ if (mA2dp != null &&
+ (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
|| BluetoothUuid.isAdvAudioDist(uuid)) &&
- !isOtherSinkInNonDisconnectingState(address)) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-
- authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
+ !isOtherSinkInNonDisconnectedState(address)) {
+ authorized = mA2dp.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
if (authorized) {
Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
mBluetoothService.notifyIncomingA2dpConnection(address);
@@ -630,9 +644,12 @@ class BluetoothEventLoop {
return false;
}
- private boolean isOtherSinkInNonDisconnectingState(String address) {
- BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
+ private boolean isOtherSinkInNonDisconnectedState(String address) {
+ Set<BluetoothDevice> devices =
+ mA2dp.getDevicesMatchingConnectionStates(new int[] {BluetoothA2dp.STATE_CONNECTED,
+ BluetoothA2dp.STATE_CONNECTING,
+ BluetoothA2dp.STATE_DISCONNECTING});
+
if (devices.size() == 0) return false;
for(BluetoothDevice dev: devices) {
if (!dev.getAddress().equals(address)) return true;
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 7f160c4a2001..bd105a74ca6c 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -30,6 +30,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothDeviceProfileState;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothSocket;
@@ -88,6 +89,7 @@ public class BluetoothService extends IBluetooth.Stub {
private int mNativeData;
private BluetoothEventLoop mEventLoop;
+ private BluetoothHeadset mBluetoothHeadset;
private boolean mIsAirplaneSensitive;
private boolean mIsAirplaneToggleable;
private int mBluetoothState;
@@ -2434,7 +2436,8 @@ public class BluetoothService extends IBluetooth.Stub {
pw.println("Local name = " + getName());
pw.println("isDiscovering() = " + isDiscovering());
- BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+ mAdapter.getProfileProxy(mContext,
+ mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
pw.println("\n--Known devices--");
for (String address : mDeviceProperties.keySet()) {
@@ -2479,24 +2482,48 @@ public class BluetoothService extends IBluetooth.Stub {
// Rather not do this from here, but no-where else and I need this
// dump
pw.println("\n--Headset Service--");
- switch (headset.getState(headset.getCurrentHeadset())) {
- case BluetoothHeadset.STATE_DISCONNECTED:
- pw.println("getState() = STATE_DISCONNECTED");
- break;
- case BluetoothHeadset.STATE_CONNECTING:
- pw.println("getState() = STATE_CONNECTING");
- break;
- case BluetoothHeadset.STATE_CONNECTED:
- pw.println("getState() = STATE_CONNECTED");
- break;
- case BluetoothHeadset.STATE_ERROR:
- pw.println("getState() = STATE_ERROR");
- break;
+ if (mBluetoothHeadset != null) {
+ Set<BluetoothDevice> deviceSet = mBluetoothHeadset.getConnectedDevices();
+ if (deviceSet.size() == 0) {
+ pw.println("\n--No headsets connected--");
+ }
+ BluetoothDevice device = (BluetoothDevice) deviceSet.toArray()[0];
+
+ switch (mBluetoothHeadset.getConnectionState(device)) {
+ case BluetoothHeadset.STATE_DISCONNECTED:
+ pw.println("getConnectionState() = STATE_DISCONNECTED");
+ break;
+ case BluetoothHeadset.STATE_CONNECTING:
+ pw.println("getConnectionState() = STATE_CONNECTING");
+ break;
+ case BluetoothHeadset.STATE_CONNECTED:
+ pw.println("getConnectionState() = STATE_CONNECTED");
+ break;
+ case BluetoothHeadset.STATE_DISCONNECTING:
+ pw.println("getConnectionState() = STATE_DISCONNECTING");
+ break;
+ case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+ pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
+ break;
+ }
+
+ deviceSet.clear();
+ deviceSet = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
+ pw.println("\n--Connected and Disconnected Headsets");
+ for (BluetoothDevice dev: deviceSet) {
+ pw.println(device);
+ if (mBluetoothHeadset.isAudioConnected(device)) {
+ pw.println("SCO audio connected to device:" + device);
+ }
+ }
+
+ pw.println("\ngetCurrentHeadset() = " + device);
+ pw.println("getBatteryUsageHint() = " +
+ mBluetoothHeadset.getBatteryUsageHint(device));
+ mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
}
- pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
- pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
- headset.close();
pw.println("\n--Application Service Records--");
for (Integer handle : mServiceRecordToPid.keySet()) {
Integer pid = mServiceRecordToPid.get(handle);
@@ -2504,6 +2531,16 @@ public class BluetoothService extends IBluetooth.Stub {
}
}
+ private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ }
+ public void onServiceDisconnected(int profile) {
+ mBluetoothHeadset = null;
+ }
+ };
+
/* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
if (pairable && discoverable)
return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
@@ -2563,6 +2600,8 @@ public class BluetoothService extends IBluetooth.Stub {
}
public boolean connectHeadset(String address) {
+ if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
@@ -2575,6 +2614,8 @@ public class BluetoothService extends IBluetooth.Stub {
}
public boolean disconnectHeadset(String address) {
+ if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
@@ -2587,6 +2628,8 @@ public class BluetoothService extends IBluetooth.Stub {
}
public boolean connectSink(String address) {
+ if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();
@@ -2599,6 +2642,8 @@ public class BluetoothService extends IBluetooth.Stub {
}
public boolean disconnectSink(String address) {
+ if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
if (state != null) {
Message msg = new Message();