Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +64 −26 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -113,6 +120,7 @@ class AvrcpControllerStateMachine extends StateMachine { private int mAddressedPlayerId = -1; private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>(); private int mVolumeChangedNotificationsToIgnore = 0; private int mVolumeNotificationLabel = -1; GetFolderList mGetFolderList = null; Loading Loading @@ -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); } Loading Loading @@ -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: Loading Loading @@ -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; } Loading Loading @@ -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() { Loading android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -70,6 +71,8 @@ public class AvrcpControllerStateMachineTest { @Mock private AdapterService mAdapterService; @Mock private AudioManager mAudioManager; @Mock private AvrcpControllerService mAvrcpControllerService; AvrcpControllerStateMachine mAvrcpStateMachine; Loading @@ -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(); Loading Loading @@ -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 * Loading Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +64 −26 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -113,6 +120,7 @@ class AvrcpControllerStateMachine extends StateMachine { private int mAddressedPlayerId = -1; private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>(); private int mVolumeChangedNotificationsToIgnore = 0; private int mVolumeNotificationLabel = -1; GetFolderList mGetFolderList = null; Loading Loading @@ -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); } Loading Loading @@ -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: Loading Loading @@ -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; } Loading Loading @@ -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() { Loading
android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +20 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -70,6 +71,8 @@ public class AvrcpControllerStateMachineTest { @Mock private AdapterService mAdapterService; @Mock private AudioManager mAudioManager; @Mock private AvrcpControllerService mAvrcpControllerService; AvrcpControllerStateMachine mAvrcpStateMachine; Loading @@ -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(); Loading Loading @@ -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 * Loading