Adding support for Absolute Volume
Change-Id: I7bbc6f9296221ca219a50a5e377ebac9dcf5a407
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d7d8cdbe..e7e4a0f 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -388,6 +388,66 @@
}
/**
+ * Checks if Avrcp device supports the absolute volume feature.
+ *
+ * @return true if device supports absolute volume
+ * @hide
+ */
+ public boolean isAvrcpAbsoluteVolumeSupported() {
+ if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.isAvrcpAbsoluteVolumeSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Tells remote device to adjust volume. Only if absolute volume is supported.
+ *
+ * @param direction 1 to increase volume, or -1 to decrease volume
+ * @hide
+ */
+ public void adjustAvrcpAbsoluteVolume(int direction) {
+ if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.adjustAvrcpAbsoluteVolume(direction);
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
+ return;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ }
+
+ /**
+ * Tells remote device to set an absolute volume. Only if absolute volume is supported
+ *
+ * @param volume Absolute volume to be set on AVRCP side
+ * @hide
+ */
+ public void setAvrcpAbsoluteVolume(int volume) {
+ if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.setAvrcpAbsoluteVolume(volume);
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
+ return;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ }
+
+ /**
* Check if A2DP profile is streaming music.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 1f10998..26ff9e27 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -32,5 +32,8 @@
int getConnectionState(in BluetoothDevice device);
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
+ boolean isAvrcpAbsoluteVolumeSupported();
+ oneway void adjustAvrcpAbsoluteVolume(int direction);
+ oneway void setAvrcpAbsoluteVolume(int volume);
boolean isA2dpPlaying(in BluetoothDevice device);
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 14cdbb7..ef02cfd 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2387,6 +2387,35 @@
}
}
+ /**
+ * @hide
+ * Notifies AudioService that it is connected to an A2DP device that supports absolute volume,
+ * so that AudioService can send volume change events to the A2DP device, rather than handling
+ * them.
+ */
+ public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
+ IAudioService service = getService();
+ try {
+ service.avrcpSupportsAbsoluteVolume(address, support);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in avrcpSupportsAbsoluteVolume", e);
+ }
+ }
+
+ /**
+ * @hide
+ * Notifies AudioService of the volume set on the A2DP device as a callback, so AudioService
+ * is able to update the UI.
+ */
+ public void avrcpUpdateVolume(int oldVolume, int volume) {
+ IAudioService service = getService();
+ try {
+ service.avrcpUpdateVolume(oldVolume, volume);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in avrcpUpdateVolume", e);
+ }
+ }
+
/**
* {@hide}
*/
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 290866e..470c571 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -460,6 +460,12 @@
private final MediaFocusControl mMediaFocusControl;
+ // Reference to BluetoothA2dp to query for AbsoluteVolume.
+ private BluetoothA2dp mA2dp;
+ private final Object mA2dpAvrcpLock = new Object();
+ // If absolute volume is supported in AVRCP device
+ private boolean mAvrcpAbsVolSupported = false;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -901,6 +907,15 @@
int oldIndex = mStreamStates[streamType].getIndex(device);
if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
+ // Check if volume update should be send to AVRCP
+ synchronized (mA2dpAvrcpLock) {
+ if (mA2dp != null && mAvrcpAbsVolSupported) {
+ mA2dp.adjustAvrcpAbsoluteVolume(direction);
+ return;
+ // No need to send volume update, because we will update the volume with a
+ // callback from Avrcp.
+ }
+ }
if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
@@ -998,6 +1013,15 @@
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
+ synchronized (mA2dpAvrcpLock) {
+ if (mA2dp != null && mAvrcpAbsVolSupported) {
+ mA2dp.setAvrcpAbsoluteVolume(index);
+ return;
+ // No need to send volume update, because we will update the volume with a
+ // callback from Avrcp.
+ }
+ }
+
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
@@ -2268,21 +2292,23 @@
List<BluetoothDevice> deviceList;
switch(profile) {
case BluetoothProfile.A2DP:
- BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
- deviceList = a2dp.getConnectedDevices();
- if (deviceList.size() > 0) {
- btDevice = deviceList.get(0);
- synchronized (mConnectedDevices) {
- int state = a2dp.getConnectionState(btDevice);
- int delay = checkSendBecomingNoisyIntent(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
- queueMsgUnderWakeLock(mAudioHandler,
- MSG_SET_A2DP_CONNECTION_STATE,
- state,
- 0,
- btDevice,
- delay);
+ synchronized (mA2dpAvrcpLock) {
+ mA2dp = (BluetoothA2dp) proxy;
+ deviceList = mA2dp.getConnectedDevices();
+ if (deviceList.size() > 0) {
+ btDevice = deviceList.get(0);
+ synchronized (mConnectedDevices) {
+ int state = mA2dp.getConnectionState(btDevice);
+ int delay = checkSendBecomingNoisyIntent(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
+ queueMsgUnderWakeLock(mAudioHandler,
+ MSG_SET_A2DP_CONNECTION_STATE,
+ state,
+ 0,
+ btDevice,
+ delay);
+ }
}
}
break;
@@ -2344,10 +2370,13 @@
public void onServiceDisconnected(int profile) {
switch(profile) {
case BluetoothProfile.A2DP:
- synchronized (mConnectedDevices) {
- if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
- makeA2dpDeviceUnavailableNow(
- mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ synchronized (mA2dpAvrcpLock) {
+ mA2dp = null;
+ synchronized (mConnectedDevices) {
+ if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
+ makeA2dpDeviceUnavailableNow(
+ mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ }
}
}
break;
@@ -3697,6 +3726,7 @@
private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
{
+ if (DEBUG_VOL) Log.d(TAG, "onSetA2dpConnectionState btDevice="+btDevice+" state="+state);
if (btDevice == null) {
return;
}
@@ -3704,6 +3734,20 @@
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
+
+ // Disable absolute volume, if device is disconnected
+ synchronized (mA2dpAvrcpLock) {
+ if (state == BluetoothProfile.STATE_DISCONNECTED && mAvrcpAbsVolSupported) {
+ mAvrcpAbsVolSupported = false;
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_QUEUE,
+ getDeviceForStream(AudioSystem.STREAM_MUSIC),
+ 0,
+ mStreamStates[AudioSystem.STREAM_MUSIC],
+ 0);
+ }
+ }
synchronized (mConnectedDevices) {
boolean isConnected =
(mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
@@ -3754,6 +3798,31 @@
}
}
+ public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
+ // address is not used for now, but may be used when multiple a2dp devices are supported
+ synchronized (mA2dpAvrcpLock) {
+ mAvrcpAbsVolSupported = support;
+ if (support) {
+ VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
+ int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
+ streamState.setIndex(streamState.getMaxIndex(), device);
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ streamState,
+ 0);
+ }
+ }
+ }
+
+ public void avrcpUpdateVolume(int oldVolume, int volume) {
+ mStreamStates[AudioSystem.STREAM_MUSIC].
+ setIndex(volume, getDeviceForStream(AudioSystem.STREAM_MUSIC));
+ sendVolumeUpdate(AudioSystem.STREAM_MUSIC, oldVolume, volume, AudioManager.FLAG_SHOW_UI);
+ }
+
private boolean handleDeviceConnection(boolean connected, int device, String params) {
synchronized (mConnectedDevices) {
boolean isConnected = (mConnectedDevices.containsKey(device) &&
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index b4c8a04..903927b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -98,6 +98,10 @@
oneway void reloadAudioSettings();
+ oneway void avrcpSupportsAbsoluteVolume(String address, boolean support);
+
+ oneway void avrcpUpdateVolume(int oldVolume, int volume);
+
void setSpeakerphoneOn(boolean on);
boolean isSpeakerphoneOn();