diff options
| -rw-r--r-- | core/java/android/bluetooth/BluetoothDeviceProfileState.java | 107 | ||||
| -rw-r--r-- | core/java/android/bluetooth/BluetoothProfileState.java | 20 | ||||
| -rw-r--r-- | core/java/android/bluetooth/IBluetoothA2dp.aidl | 24 | ||||
| -rw-r--r-- | core/java/android/bluetooth/IBluetoothHeadset.aidl | 25 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothA2dpService.java | 161 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 33 | ||||
| -rw-r--r-- | core/java/android/server/BluetoothService.java | 79 |
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(); |