From 7c3a74093b155ffea85038c75621fb73a1a46b36 Mon Sep 17 00:00:00 2001 From: Joseph Pirozzo Date: Mon, 24 Jun 2019 17:08:50 -0700 Subject: AVRCP Controller AbsoluteVolumeNotification Update AbsoluteVolumeNotification request to properly respond to requests based upon the Fixed vs Dynamic volume levels. Bug: 129478624 Test: atest com.android.bluetooth.avrcpcontroller.AvrcpControllerStateMachineTest#testRegisterAbsVolumeNotification Change-Id: Ic9b745641a84712c005d40ecec50e55e9684d3ef (cherry picked from commit 8fd119d8a27d42e09a90280020925859fd0a1678) Merged-In: Ic9b745641a84712c005d40ecec50e55e9684d3ef Change-Id: I1212a70ab84e3aed926bcfcac04dc62efed4b489 --- .../AvrcpControllerStateMachine.java | 90 +++++++++++++++------- .../AvrcpControllerStateMachineTest.java | 20 +++++ 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java index 7a6c54126..83ba8505d 100644 --- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java @@ -94,7 +94,14 @@ class AvrcpControllerStateMachine extends StateMachine { */ private static final int ABS_VOL_BASE = 127; + /* + * Notification types for Avrcp protocol JNI. + */ + private static final byte NOTIFICATION_RSP_TYPE_INTERIM = 0x00; + private static final byte NOTIFICATION_RSP_TYPE_CHANGED = 0x01; + private final AudioManager mAudioManager; + private final boolean mIsVolumeFixed; protected final BluetoothDevice mDevice; protected final byte[] mDeviceAddress; @@ -113,6 +120,7 @@ class AvrcpControllerStateMachine extends StateMachine { private int mAddressedPlayerId = -1; private SparseArray mAvailablePlayerList = new SparseArray(); private int mVolumeChangedNotificationsToIgnore = 0; + private int mVolumeNotificationLabel = -1; GetFolderList mGetFolderList = null; @@ -142,6 +150,7 @@ class AvrcpControllerStateMachine extends StateMachine { mGetFolderList = new GetFolderList(); addState(mGetFolderList, mConnected); mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE); + mIsVolumeFixed = mAudioManager.isVolumeFixed(); setInitialState(mDisconnected); } @@ -311,7 +320,14 @@ class AvrcpControllerStateMachine extends StateMachine { removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, ABS_VOL_TIMEOUT_MILLIS); - setAbsVolume(msg.arg1, msg.arg2); + handleAbsVolumeRequest(msg.arg1, msg.arg2); + return true; + + case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: + mVolumeNotificationLabel = msg.arg1; + mService.sendRegisterAbsVolRspNative(mDeviceAddress, + NOTIFICATION_RSP_TYPE_INTERIM, + getAbsVolume(), mVolumeNotificationLabel); return true; case MESSAGE_GET_FOLDER_ITEMS: @@ -587,24 +603,9 @@ class AvrcpControllerStateMachine extends StateMachine { } break; - case CONNECT: - case DISCONNECT: - case MSG_AVRCP_PASSTHRU: - case MESSAGE_PROCESS_SET_ABS_VOL_CMD: - case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: - case MESSAGE_PROCESS_TRACK_CHANGED: - case MESSAGE_PROCESS_PLAY_POS_CHANGED: - case MESSAGE_PROCESS_PLAY_STATUS_CHANGED: - case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION: - case MESSAGE_PLAY_ITEM: - case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED: + default: // All of these messages should be handled by parent state immediately. return false; - - default: - logD(STATE_TAG + " deferring message " + msg.what - + " to connected!"); - deferMessage(msg); } return true; } @@ -717,23 +718,60 @@ class AvrcpControllerStateMachine extends StateMachine { } } + /** + * Handle a request to align our local volume with the volume of a remote device. If + * we're assuming the source volume is fixed then a response of ABS_VOL_MAX will always be + * sent and no volume adjustment action will be taken on the sink side. + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + * @param label Volume notification label + */ + private void handleAbsVolumeRequest(int absVol, int label) { + logD("handleAbsVolumeRequest: absVol = " + absVol + ", label = " + label); + if (mIsVolumeFixed) { + logD("Source volume is assumed to be fixed, responding with max volume"); + absVol = ABS_VOL_BASE; + } else { + mVolumeChangedNotificationsToIgnore++; + removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); + sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, + ABS_VOL_TIMEOUT_MILLIS); + setAbsVolume(absVol); + } + mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); + } + + /** + * Align our volume with a requested absolute volume level + * + * @param absVol A volume level based on a domain of [0, ABS_VOL_MAX] + */ + private void setAbsVolume(int absVol) { + int maxLocalVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int curLocalVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + int reqLocalVolume = (maxLocalVolume * absVol) / ABS_VOL_BASE; + logD("setAbsVolme: absVol = " + absVol + ", reqLocal = " + reqLocalVolume + + ", curLocal = " + curLocalVolume + ", maxLocal = " + maxLocalVolume); - private void setAbsVolume(int absVol, int label) { - int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); - int newIndex = (maxVolume * absVol) / ABS_VOL_BASE; - logD(" setAbsVolume =" + absVol + " maxVol = " + maxVolume - + " cur = " + currIndex + " new = " + newIndex); /* * In some cases change in percentage is not sufficient enough to warrant * change in index values which are in range of 0-15. For such cases * no action is required */ - if (newIndex != currIndex) { - mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex, + if (reqLocalVolume != curLocalVolume) { + mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, reqLocalVolume, AudioManager.FLAG_SHOW_UI); } - mService.sendAbsVolRspNative(mDeviceAddress, absVol, label); + } + + private int getAbsVolume() { + if (mIsVolumeFixed) { + return ABS_VOL_BASE; + } + int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + int newIndex = (currIndex * ABS_VOL_BASE) / maxVolume; + return newIndex; } MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() { diff --git a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java index 0ebb31e9e..4c77e0afa 100644 --- a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +++ b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; +import android.media.AudioManager; import android.os.Looper; import android.support.v4.media.session.MediaControllerCompat; @@ -70,6 +71,8 @@ public class AvrcpControllerStateMachineTest { @Mock private AdapterService mAdapterService; @Mock + private AudioManager mAudioManager; + @Mock private AvrcpControllerService mAvrcpControllerService; AvrcpControllerStateMachine mAvrcpStateMachine; @@ -90,6 +93,11 @@ public class AvrcpControllerStateMachineTest { TestUtils.setAdapterService(mAdapterService); TestUtils.startService(mServiceRule, AvrcpControllerService.class); doReturn(mTargetContext.getResources()).when(mAvrcpControllerService).getResources(); + doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt()); + doReturn(8).when(mAudioManager).getStreamVolume(anyInt()); + doReturn(true).when(mAudioManager).isVolumeFixed(); + doReturn(mAudioManager).when(mAvrcpControllerService) + .getSystemService(Context.AUDIO_SERVICE); // This line must be called to make sure relevant objects are initialized properly mAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -524,6 +532,18 @@ public class AvrcpControllerStateMachineTest { eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP)); } + /** + * Test that Absolute Volume Registration is working + */ + @Test + public void testRegisterAbsVolumeNotification() { + setUpConnectedState(true, true); + mAvrcpStateMachine.sendMessage( + AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION); + verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) + .sendRegisterAbsVolRspNative(any(), anyByte(), eq(127), anyInt()); + } + /** * Setup Connected State * -- cgit v1.2.3