Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +46 −26 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.bluetooth.Utils; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; import java.util.ArrayList; Loading Loading @@ -67,7 +68,8 @@ public class AvrcpControllerService extends ProfileService { private static final byte JNI_PLAY_STATUS_PLAYING = 0x01; private static final byte JNI_PLAY_STATUS_PAUSED = 0x02; private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03; private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04; @VisibleForTesting static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04; private static final byte JNI_PLAY_STATUS_ERROR = -1; /* Folder/Media Item scopes. Loading Loading @@ -173,6 +175,7 @@ public class AvrcpControllerService extends ProfileService { for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { stateMachine.quitNow(); } mDeviceStateMap.clear(); sService = null; sBrowseTree = null; Loading Loading @@ -201,7 +204,8 @@ public class AvrcpControllerService extends ProfileService { /** * Set the current active device, notify devices of activity status */ private boolean setActiveDevice(BluetoothDevice device) { @VisibleForTesting boolean setActiveDevice(BluetoothDevice device) { A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; Loading Loading @@ -277,7 +281,8 @@ public class AvrcpControllerService extends ProfileService { } } private void refreshContents(BrowseTree.BrowseNode node) { @VisibleForTesting void refreshContents(BrowseTree.BrowseNode node) { BluetoothDevice device = node.getDevice(); if (device == null) { return; Loading Loading @@ -467,14 +472,16 @@ public class AvrcpControllerService extends ProfileService { /* JNI API*/ // Called by JNI when a passthrough key was received. private void handlePassthroughRsp(int id, int keyState, byte[] address) { @VisibleForTesting void handlePassthroughRsp(int id, int keyState, byte[] address) { if (DBG) { Log.d(TAG, "passthrough response received as: key: " + id + " state: " + keyState + "address:" + Arrays.toString(address)); } } private void handleGroupNavigationRsp(int id, int keyState) { @VisibleForTesting void handleGroupNavigationRsp(int id, int keyState) { if (DBG) { Log.d(TAG, "group navigation response received as: key: " + id + " state: " + keyState); Loading @@ -482,17 +489,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when a device has connected or disconnected. private synchronized void onConnectionStateChanged(boolean remoteControlConnected, @VisibleForTesting synchronized void onConnectionStateChanged(boolean remoteControlConnected, boolean browsingConnected, byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onConnectionStateChanged " + remoteControlConnected + " " + browsingConnected + device); } if (device == null) { Log.e(TAG, "onConnectionStateChanged Device is null"); return; } StackEvent event = StackEvent.connectionStateChanged(remoteControlConnected, browsingConnected); Loading @@ -512,12 +516,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI to notify Avrcp of features supported by the Remote device. private void getRcFeatures(byte[] address, int features) { @VisibleForTesting void getRcFeatures(byte[] address, int features) { /* Do Nothing. */ } // Called by JNI to notify Avrcp of a remote device's Cover Art PSM private void getRcPsm(byte[] address, int psm) { @VisibleForTesting void getRcPsm(byte[] address, int psm) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) Log.d(TAG, "getRcPsm(device=" + device + ", psm=" + psm + ")"); AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); Loading @@ -528,12 +534,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI private void setPlayerAppSettingRsp(byte[] address, byte accepted) { @VisibleForTesting void setPlayerAppSettingRsp(byte[] address, byte accepted) { /* Do Nothing. */ } // Called by JNI when remote wants to receive absolute volume notifications. private synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { @VisibleForTesting synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { if (DBG) { Log.d(TAG, "handleRegisterNotificationAbsVol"); } Loading @@ -546,7 +554,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when remote wants to set absolute volume. private synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { @VisibleForTesting synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { if (DBG) { Log.d(TAG, "handleSetAbsVolume "); } Loading @@ -559,7 +568,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when a track changes and local AvrcpController is registered for updates. private synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, @VisibleForTesting synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) { if (DBG) { Log.d(TAG, "onTrackChanged"); Loading @@ -586,7 +596,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI periodically based upon timer to update play position private synchronized void onPlayPositionChanged(byte[] address, int songLen, @VisibleForTesting synchronized void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) { if (DBG) { Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition); Loading @@ -601,7 +612,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI on changes of play status private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) { @VisibleForTesting synchronized void onPlayStatusChanged(byte[] address, byte playStatus) { if (DBG) { Log.d(TAG, "onPlayStatusChanged " + playStatus); } Loading @@ -615,7 +627,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI to report remote Player's capabilities private synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, @VisibleForTesting synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) { if (DBG) { Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen); Loading @@ -631,7 +644,8 @@ public class AvrcpControllerService extends ProfileService { } } private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, @VisibleForTesting synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) { if (DBG) { Log.d(TAG, "onPlayerAppSettingChanged "); Loading @@ -648,7 +662,8 @@ public class AvrcpControllerService extends ProfileService { } } private void onAvailablePlayerChanged(byte[] address) { @VisibleForTesting void onAvailablePlayerChanged(byte[] address) { if (DBG) { Log.d(TAG," onAvailablePlayerChanged"); } Loading Loading @@ -766,7 +781,8 @@ public class AvrcpControllerService extends ProfileService { return apb.build(); } private void handleChangeFolderRsp(byte[] address, int count) { @VisibleForTesting void handleChangeFolderRsp(byte[] address, int count) { if (DBG) { Log.d(TAG, "handleChangeFolderRsp count: " + count); } Loading @@ -778,7 +794,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { @VisibleForTesting void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { if (DBG) { Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth); } Loading @@ -791,7 +808,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleSetAddressedPlayerRsp(byte[] address, int status) { @VisibleForTesting void handleSetAddressedPlayerRsp(byte[] address, int status) { if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status); } Loading @@ -804,7 +822,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleAddressedPlayerChanged(byte[] address, int id) { @VisibleForTesting void handleAddressedPlayerChanged(byte[] address, int id) { if (DBG) { Log.d(TAG, "handleAddressedPlayerChanged id: " + id); } Loading @@ -817,7 +836,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleNowPlayingContentChanged(byte[] address) { @VisibleForTesting void handleNowPlayingContentChanged(byte[] address) { if (DBG) { Log.d(TAG, "handleNowPlayingContentChanged"); } Loading android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java +357 −11 Original line number Diff line number Diff line Loading @@ -15,45 +15,59 @@ */ package com.android.bluetooth.avrcpcontroller; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.support.v4.media.session.PlaybackStateCompat; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; @MediumTest @RunWith(AndroidJUnit4.class) public class AvrcpControllerServiceTest { private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00"; private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0}; private AvrcpControllerService mService = null; private BluetoothAdapter mAdapter = null; private Context mTargetContext; @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); @Mock private AdapterService mAdapterService; @Mock private AvrcpControllerStateMachine mStateMachine; private BluetoothDevice mRemoteDevice; @Before public void setUp() throws Exception { mTargetContext = InstrumentationRegistry.getTargetContext(); Assume.assumeTrue("Ignore test when AvrcpControllerService is not enabled", AvrcpControllerService.isEnabled()); MockitoAnnotations.initMocks(this); Loading @@ -61,10 +75,12 @@ public class AvrcpControllerServiceTest { doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); TestUtils.startService(mServiceRule, AvrcpControllerService.class); mService = AvrcpControllerService.getAvrcpControllerService(); Assert.assertNotNull(mService); assertThat(mService).isNotNull(); // Try getting the Bluetooth adapter mAdapter = BluetoothAdapter.getDefaultAdapter(); Assert.assertNotNull(mAdapter); assertThat(mAdapter).isNotNull(); mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); mService.mDeviceStateMap.put(mRemoteDevice, mStateMachine); } @After Loading @@ -74,12 +90,342 @@ public class AvrcpControllerServiceTest { } TestUtils.stopService(mServiceRule, AvrcpControllerService.class); mService = AvrcpControllerService.getAvrcpControllerService(); Assert.assertNull(mService); assertThat(mService).isNull(); TestUtils.clearAdapterService(mAdapterService); } @Test public void testInitialize() { Assert.assertNotNull(AvrcpControllerService.getAvrcpControllerService()); public void initialize() { assertThat(AvrcpControllerService.getAvrcpControllerService()).isNotNull(); } @Test public void disconnect_whenDisconnected_returnsFalse() { when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.disconnect(mRemoteDevice)).isFalse(); } @Test public void disconnect_whenDisconnected_returnsTrue() { when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mService.disconnect(mRemoteDevice)).isTrue(); verify(mStateMachine).disconnect(); } @Test public void removeStateMachine() { when(mStateMachine.getDevice()).thenReturn(mRemoteDevice); mService.removeStateMachine(mStateMachine); assertThat(mService.mDeviceStateMap).doesNotContainKey(mRemoteDevice); } @Test public void getConnectedDevices() { when(mAdapterService.getBondedDevices()).thenReturn( new BluetoothDevice[]{mRemoteDevice}); when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices()).contains(mRemoteDevice); } @Test public void setActiveDevice_whenA2dpSinkServiceIsNotInitailized_returnsFalse() { assertThat(mService.setActiveDevice(mRemoteDevice)).isFalse(); assertThat(mService.getActiveDevice()).isNull(); } @Test public void getCurrentMetadataIfNoCoverArt_doesNotCrash() { mService.getCurrentMetadataIfNoCoverArt(mRemoteDevice); } @Test public void refreshContents() { BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(node.getDevice()).thenReturn(mRemoteDevice); mService.refreshContents(node); verify(mStateMachine).requestContents(node); } @Test public void playItem() { String parentMediaId = "test_parent_media_id"; BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(mStateMachine.findNode(parentMediaId)).thenReturn(node); mService.playItem(parentMediaId); verify(mStateMachine).playItem(node); } @Test public void getContents() { String parentMediaId = "test_parent_media_id"; BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(mStateMachine.findNode(parentMediaId)).thenReturn(node); mService.getContents(parentMediaId); verify(node).getContents(); } @Test public void createFromNativeMediaItem() { long uid = 1; int type = 2; int[] attrIds = new int[] { 0x01 }; // MEDIA_ATTRIBUTE_TITLE} String[] attrVals = new String[] {"test_title"}; AvrcpItem item = mService.createFromNativeMediaItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, "unused_name", attrIds, attrVals); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA); assertThat(item.getType()).isEqualTo(type); assertThat(item.getUid()).isEqualTo(uid); assertThat(item.getUuid()).isNotNull(); // Random uuid assertThat(item.getTitle()).isEqualTo(attrVals[0]); assertThat(item.isPlayable()).isTrue(); } @Test public void createFromNativeFolderItem() { long uid = 1; int type = 2; String folderName = "test_folder_name"; int playable = 0x01; // Playable folder AvrcpItem item = mService.createFromNativeFolderItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, folderName, playable); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_FOLDER); assertThat(item.getType()).isEqualTo(type); assertThat(item.getUid()).isEqualTo(uid); assertThat(item.getUuid()).isNotNull(); // Random uuid assertThat(item.getDisplayableName()).isEqualTo(folderName); assertThat(item.isPlayable()).isTrue(); } @Test public void createFromNativePlayerItem() { int playerId = 1; String name = "test_name"; byte[] transportFlags = new byte[] {1, 0, 0, 0, 0, 0, 0, 0}; int playStatus = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK; int playerType = AvrcpPlayer.TYPE_AUDIO; // No getter exists AvrcpPlayer player = mService.createFromNativePlayerItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerId, name, transportFlags, playStatus, playerType); assertThat(player.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(player.getId()).isEqualTo(playerId); assertThat(player.supportsFeature(0)).isTrue(); assertThat(player.getName()).isEqualTo(name); assertThat(player.getPlayStatus()).isEqualTo(PlaybackStateCompat.STATE_REWINDING); } @Test public void handleChangeFolderRsp() { int count = 1; mService.handleChangeFolderRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, count); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, count); } @Test public void handleSetBrowsedPlayerRsp() { int items = 3; int depth = 5; mService.handleSetBrowsedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items, depth); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER, items, depth); } @Test public void handleSetAddressedPlayerRsp() { int status = 1; mService.handleSetAddressedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER); } @Test public void handleAddressedPlayerChanged() { int id = 1; mService.handleAddressedPlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, id); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id); } @Test public void handleNowPlayingContentChanged() { mService.handleNowPlayingContentChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).nowPlayingContentChanged(); } @Test public void JniApisWithNoBehaviors_doNotCrash() { mService.handlePassthroughRsp(1, 2, new byte[0]); mService.handleGroupNavigationRsp(1, 2); mService.getRcFeatures(new byte[0], 1); mService.setPlayerAppSettingRsp(new byte[0], (byte) 0); } @Test public void onConnectionStateChanged_connectCase() { boolean remoteControlConnected = true; boolean browsingConnected = true; // Calls connect when any of them is true. mService.onConnectionStateChanged(remoteControlConnected, browsingConnected, REMOTE_DEVICE_ADDRESS_AS_ARRAY); ArgumentCaptor<StackEvent> captor = ArgumentCaptor.forClass(StackEvent.class); verify(mStateMachine).connect(captor.capture()); StackEvent event = captor.getValue(); assertThat(event.mType).isEqualTo(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); assertThat(event.mRemoteControlConnected).isEqualTo(remoteControlConnected); assertThat(event.mBrowsingConnected).isEqualTo(browsingConnected); } @Test public void onConnectionStateChanged_disconnectCase() { boolean remoteControlConnected = false; boolean browsingConnected = false; // Calls disconnect when both of them are false. mService.onConnectionStateChanged( remoteControlConnected, browsingConnected, REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).disconnect(); } @Test public void getRcPsm() { int psm = 1; mService.getRcPsm(REMOTE_DEVICE_ADDRESS_AS_ARRAY, psm); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm); } @Test public void handleRegisterNotificationAbsVol() { byte label = 1; mService.handleRegisterNotificationAbsVol(REMOTE_DEVICE_ADDRESS_AS_ARRAY, label); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION); } @Test public void handleSetAbsVolume() { byte absVol = 15; byte label = 1; mService.handleSetAbsVolume(REMOTE_DEVICE_ADDRESS_AS_ARRAY, absVol, label); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol); } @Test public void onTrackChanged() { byte numAttrs = 0; int[] attrs = new int[0]; String[] attrVals = new String[0]; mService.onTrackChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, numAttrs, attrs, attrVals); ArgumentCaptor<AvrcpItem> captor = ArgumentCaptor.forClass(AvrcpItem.class); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED), captor.capture()); AvrcpItem item = captor.getValue(); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA); assertThat(item.getUuid()).isNotNull(); // Random uuid } @Test public void onPlayPositionChanged() { int songLen = 100; int currSongPos = 33; mService.onPlayPositionChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, songLen, currSongPos); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, songLen, currSongPos); } @Test public void onPlayStatusChanged() { byte status = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK; mService.onPlayStatusChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, PlaybackStateCompat.STATE_REWINDING); } @Test public void onPlayerAppSettingChanged() { byte[] playerAttribRsp = new byte[] {PlayerApplicationSettings.REPEAT_STATUS, PlayerApplicationSettings.JNI_REPEAT_STATUS_ALL_TRACK_REPEAT}; mService.onPlayerAppSettingChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerAttribRsp, 2); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS), any(PlayerApplicationSettings.class)); } @Test public void onAvailablePlayerChanged() { mService.onAvailablePlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); } @Test public void handleGetFolderItemsRsp() { int status = 2; AvrcpItem[] items = new AvrcpItem[] {mock(AvrcpItem.class)}; mService.handleGetFolderItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status, items); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS), eq(new ArrayList<>(Arrays.asList(items)))); } @Test public void handleGetPlayerItemsRsp() { AvrcpPlayer[] items = new AvrcpPlayer[] {mock(AvrcpPlayer.class)}; mService.handleGetPlayerItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS), eq(new ArrayList<>(Arrays.asList(items)))); } } Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +46 −26 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.bluetooth.Utils; import com.android.bluetooth.a2dpsink.A2dpSinkService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; import java.util.ArrayList; Loading Loading @@ -67,7 +68,8 @@ public class AvrcpControllerService extends ProfileService { private static final byte JNI_PLAY_STATUS_PLAYING = 0x01; private static final byte JNI_PLAY_STATUS_PAUSED = 0x02; private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03; private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04; @VisibleForTesting static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04; private static final byte JNI_PLAY_STATUS_ERROR = -1; /* Folder/Media Item scopes. Loading Loading @@ -173,6 +175,7 @@ public class AvrcpControllerService extends ProfileService { for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { stateMachine.quitNow(); } mDeviceStateMap.clear(); sService = null; sBrowseTree = null; Loading Loading @@ -201,7 +204,8 @@ public class AvrcpControllerService extends ProfileService { /** * Set the current active device, notify devices of activity status */ private boolean setActiveDevice(BluetoothDevice device) { @VisibleForTesting boolean setActiveDevice(BluetoothDevice device) { A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); if (a2dpSinkService == null) { return false; Loading Loading @@ -277,7 +281,8 @@ public class AvrcpControllerService extends ProfileService { } } private void refreshContents(BrowseTree.BrowseNode node) { @VisibleForTesting void refreshContents(BrowseTree.BrowseNode node) { BluetoothDevice device = node.getDevice(); if (device == null) { return; Loading Loading @@ -467,14 +472,16 @@ public class AvrcpControllerService extends ProfileService { /* JNI API*/ // Called by JNI when a passthrough key was received. private void handlePassthroughRsp(int id, int keyState, byte[] address) { @VisibleForTesting void handlePassthroughRsp(int id, int keyState, byte[] address) { if (DBG) { Log.d(TAG, "passthrough response received as: key: " + id + " state: " + keyState + "address:" + Arrays.toString(address)); } } private void handleGroupNavigationRsp(int id, int keyState) { @VisibleForTesting void handleGroupNavigationRsp(int id, int keyState) { if (DBG) { Log.d(TAG, "group navigation response received as: key: " + id + " state: " + keyState); Loading @@ -482,17 +489,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when a device has connected or disconnected. private synchronized void onConnectionStateChanged(boolean remoteControlConnected, @VisibleForTesting synchronized void onConnectionStateChanged(boolean remoteControlConnected, boolean browsingConnected, byte[] address) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) { Log.d(TAG, "onConnectionStateChanged " + remoteControlConnected + " " + browsingConnected + device); } if (device == null) { Log.e(TAG, "onConnectionStateChanged Device is null"); return; } StackEvent event = StackEvent.connectionStateChanged(remoteControlConnected, browsingConnected); Loading @@ -512,12 +516,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI to notify Avrcp of features supported by the Remote device. private void getRcFeatures(byte[] address, int features) { @VisibleForTesting void getRcFeatures(byte[] address, int features) { /* Do Nothing. */ } // Called by JNI to notify Avrcp of a remote device's Cover Art PSM private void getRcPsm(byte[] address, int psm) { @VisibleForTesting void getRcPsm(byte[] address, int psm) { BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (DBG) Log.d(TAG, "getRcPsm(device=" + device + ", psm=" + psm + ")"); AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); Loading @@ -528,12 +534,14 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI private void setPlayerAppSettingRsp(byte[] address, byte accepted) { @VisibleForTesting void setPlayerAppSettingRsp(byte[] address, byte accepted) { /* Do Nothing. */ } // Called by JNI when remote wants to receive absolute volume notifications. private synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { @VisibleForTesting synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { if (DBG) { Log.d(TAG, "handleRegisterNotificationAbsVol"); } Loading @@ -546,7 +554,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when remote wants to set absolute volume. private synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { @VisibleForTesting synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { if (DBG) { Log.d(TAG, "handleSetAbsVolume "); } Loading @@ -559,7 +568,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI when a track changes and local AvrcpController is registered for updates. private synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, @VisibleForTesting synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) { if (DBG) { Log.d(TAG, "onTrackChanged"); Loading @@ -586,7 +596,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI periodically based upon timer to update play position private synchronized void onPlayPositionChanged(byte[] address, int songLen, @VisibleForTesting synchronized void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) { if (DBG) { Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition); Loading @@ -601,7 +612,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI on changes of play status private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) { @VisibleForTesting synchronized void onPlayStatusChanged(byte[] address, byte playStatus) { if (DBG) { Log.d(TAG, "onPlayStatusChanged " + playStatus); } Loading @@ -615,7 +627,8 @@ public class AvrcpControllerService extends ProfileService { } // Called by JNI to report remote Player's capabilities private synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, @VisibleForTesting synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) { if (DBG) { Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen); Loading @@ -631,7 +644,8 @@ public class AvrcpControllerService extends ProfileService { } } private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, @VisibleForTesting synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) { if (DBG) { Log.d(TAG, "onPlayerAppSettingChanged "); Loading @@ -648,7 +662,8 @@ public class AvrcpControllerService extends ProfileService { } } private void onAvailablePlayerChanged(byte[] address) { @VisibleForTesting void onAvailablePlayerChanged(byte[] address) { if (DBG) { Log.d(TAG," onAvailablePlayerChanged"); } Loading Loading @@ -766,7 +781,8 @@ public class AvrcpControllerService extends ProfileService { return apb.build(); } private void handleChangeFolderRsp(byte[] address, int count) { @VisibleForTesting void handleChangeFolderRsp(byte[] address, int count) { if (DBG) { Log.d(TAG, "handleChangeFolderRsp count: " + count); } Loading @@ -778,7 +794,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { @VisibleForTesting void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { if (DBG) { Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth); } Loading @@ -791,7 +808,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleSetAddressedPlayerRsp(byte[] address, int status) { @VisibleForTesting void handleSetAddressedPlayerRsp(byte[] address, int status) { if (DBG) { Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status); } Loading @@ -804,7 +822,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleAddressedPlayerChanged(byte[] address, int id) { @VisibleForTesting void handleAddressedPlayerChanged(byte[] address, int id) { if (DBG) { Log.d(TAG, "handleAddressedPlayerChanged id: " + id); } Loading @@ -817,7 +836,8 @@ public class AvrcpControllerService extends ProfileService { } } private void handleNowPlayingContentChanged(byte[] address) { @VisibleForTesting void handleNowPlayingContentChanged(byte[] address) { if (DBG) { Log.d(TAG, "handleNowPlayingContentChanged"); } Loading
android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java +357 −11 Original line number Diff line number Diff line Loading @@ -15,45 +15,59 @@ */ package com.android.bluetooth.avrcpcontroller; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.support.v4.media.session.PlaybackStateCompat; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.rule.ServiceTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; @MediumTest @RunWith(AndroidJUnit4.class) public class AvrcpControllerServiceTest { private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00"; private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0}; private AvrcpControllerService mService = null; private BluetoothAdapter mAdapter = null; private Context mTargetContext; @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); @Mock private AdapterService mAdapterService; @Mock private AvrcpControllerStateMachine mStateMachine; private BluetoothDevice mRemoteDevice; @Before public void setUp() throws Exception { mTargetContext = InstrumentationRegistry.getTargetContext(); Assume.assumeTrue("Ignore test when AvrcpControllerService is not enabled", AvrcpControllerService.isEnabled()); MockitoAnnotations.initMocks(this); Loading @@ -61,10 +75,12 @@ public class AvrcpControllerServiceTest { doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); TestUtils.startService(mServiceRule, AvrcpControllerService.class); mService = AvrcpControllerService.getAvrcpControllerService(); Assert.assertNotNull(mService); assertThat(mService).isNotNull(); // Try getting the Bluetooth adapter mAdapter = BluetoothAdapter.getDefaultAdapter(); Assert.assertNotNull(mAdapter); assertThat(mAdapter).isNotNull(); mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS); mService.mDeviceStateMap.put(mRemoteDevice, mStateMachine); } @After Loading @@ -74,12 +90,342 @@ public class AvrcpControllerServiceTest { } TestUtils.stopService(mServiceRule, AvrcpControllerService.class); mService = AvrcpControllerService.getAvrcpControllerService(); Assert.assertNull(mService); assertThat(mService).isNull(); TestUtils.clearAdapterService(mAdapterService); } @Test public void testInitialize() { Assert.assertNotNull(AvrcpControllerService.getAvrcpControllerService()); public void initialize() { assertThat(AvrcpControllerService.getAvrcpControllerService()).isNotNull(); } @Test public void disconnect_whenDisconnected_returnsFalse() { when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED); assertThat(mService.disconnect(mRemoteDevice)).isFalse(); } @Test public void disconnect_whenDisconnected_returnsTrue() { when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mService.disconnect(mRemoteDevice)).isTrue(); verify(mStateMachine).disconnect(); } @Test public void removeStateMachine() { when(mStateMachine.getDevice()).thenReturn(mRemoteDevice); mService.removeStateMachine(mStateMachine); assertThat(mService.mDeviceStateMap).doesNotContainKey(mRemoteDevice); } @Test public void getConnectedDevices() { when(mAdapterService.getBondedDevices()).thenReturn( new BluetoothDevice[]{mRemoteDevice}); when(mStateMachine.getState()).thenReturn(BluetoothProfile.STATE_CONNECTED); assertThat(mService.getConnectedDevices()).contains(mRemoteDevice); } @Test public void setActiveDevice_whenA2dpSinkServiceIsNotInitailized_returnsFalse() { assertThat(mService.setActiveDevice(mRemoteDevice)).isFalse(); assertThat(mService.getActiveDevice()).isNull(); } @Test public void getCurrentMetadataIfNoCoverArt_doesNotCrash() { mService.getCurrentMetadataIfNoCoverArt(mRemoteDevice); } @Test public void refreshContents() { BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(node.getDevice()).thenReturn(mRemoteDevice); mService.refreshContents(node); verify(mStateMachine).requestContents(node); } @Test public void playItem() { String parentMediaId = "test_parent_media_id"; BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(mStateMachine.findNode(parentMediaId)).thenReturn(node); mService.playItem(parentMediaId); verify(mStateMachine).playItem(node); } @Test public void getContents() { String parentMediaId = "test_parent_media_id"; BrowseTree.BrowseNode node = mock(BrowseTree.BrowseNode.class); when(mStateMachine.findNode(parentMediaId)).thenReturn(node); mService.getContents(parentMediaId); verify(node).getContents(); } @Test public void createFromNativeMediaItem() { long uid = 1; int type = 2; int[] attrIds = new int[] { 0x01 }; // MEDIA_ATTRIBUTE_TITLE} String[] attrVals = new String[] {"test_title"}; AvrcpItem item = mService.createFromNativeMediaItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, "unused_name", attrIds, attrVals); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA); assertThat(item.getType()).isEqualTo(type); assertThat(item.getUid()).isEqualTo(uid); assertThat(item.getUuid()).isNotNull(); // Random uuid assertThat(item.getTitle()).isEqualTo(attrVals[0]); assertThat(item.isPlayable()).isTrue(); } @Test public void createFromNativeFolderItem() { long uid = 1; int type = 2; String folderName = "test_folder_name"; int playable = 0x01; // Playable folder AvrcpItem item = mService.createFromNativeFolderItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, folderName, playable); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_FOLDER); assertThat(item.getType()).isEqualTo(type); assertThat(item.getUid()).isEqualTo(uid); assertThat(item.getUuid()).isNotNull(); // Random uuid assertThat(item.getDisplayableName()).isEqualTo(folderName); assertThat(item.isPlayable()).isTrue(); } @Test public void createFromNativePlayerItem() { int playerId = 1; String name = "test_name"; byte[] transportFlags = new byte[] {1, 0, 0, 0, 0, 0, 0, 0}; int playStatus = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK; int playerType = AvrcpPlayer.TYPE_AUDIO; // No getter exists AvrcpPlayer player = mService.createFromNativePlayerItem( REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerId, name, transportFlags, playStatus, playerType); assertThat(player.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(player.getId()).isEqualTo(playerId); assertThat(player.supportsFeature(0)).isTrue(); assertThat(player.getName()).isEqualTo(name); assertThat(player.getPlayStatus()).isEqualTo(PlaybackStateCompat.STATE_REWINDING); } @Test public void handleChangeFolderRsp() { int count = 1; mService.handleChangeFolderRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, count); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, count); } @Test public void handleSetBrowsedPlayerRsp() { int items = 3; int depth = 5; mService.handleSetBrowsedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items, depth); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER, items, depth); } @Test public void handleSetAddressedPlayerRsp() { int status = 1; mService.handleSetAddressedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER); } @Test public void handleAddressedPlayerChanged() { int id = 1; mService.handleAddressedPlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, id); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id); } @Test public void handleNowPlayingContentChanged() { mService.handleNowPlayingContentChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).nowPlayingContentChanged(); } @Test public void JniApisWithNoBehaviors_doNotCrash() { mService.handlePassthroughRsp(1, 2, new byte[0]); mService.handleGroupNavigationRsp(1, 2); mService.getRcFeatures(new byte[0], 1); mService.setPlayerAppSettingRsp(new byte[0], (byte) 0); } @Test public void onConnectionStateChanged_connectCase() { boolean remoteControlConnected = true; boolean browsingConnected = true; // Calls connect when any of them is true. mService.onConnectionStateChanged(remoteControlConnected, browsingConnected, REMOTE_DEVICE_ADDRESS_AS_ARRAY); ArgumentCaptor<StackEvent> captor = ArgumentCaptor.forClass(StackEvent.class); verify(mStateMachine).connect(captor.capture()); StackEvent event = captor.getValue(); assertThat(event.mType).isEqualTo(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); assertThat(event.mRemoteControlConnected).isEqualTo(remoteControlConnected); assertThat(event.mBrowsingConnected).isEqualTo(browsingConnected); } @Test public void onConnectionStateChanged_disconnectCase() { boolean remoteControlConnected = false; boolean browsingConnected = false; // Calls disconnect when both of them are false. mService.onConnectionStateChanged( remoteControlConnected, browsingConnected, REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).disconnect(); } @Test public void getRcPsm() { int psm = 1; mService.getRcPsm(REMOTE_DEVICE_ADDRESS_AS_ARRAY, psm); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm); } @Test public void handleRegisterNotificationAbsVol() { byte label = 1; mService.handleRegisterNotificationAbsVol(REMOTE_DEVICE_ADDRESS_AS_ARRAY, label); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION); } @Test public void handleSetAbsVolume() { byte absVol = 15; byte label = 1; mService.handleSetAbsVolume(REMOTE_DEVICE_ADDRESS_AS_ARRAY, absVol, label); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol); } @Test public void onTrackChanged() { byte numAttrs = 0; int[] attrs = new int[0]; String[] attrVals = new String[0]; mService.onTrackChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, numAttrs, attrs, attrVals); ArgumentCaptor<AvrcpItem> captor = ArgumentCaptor.forClass(AvrcpItem.class); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED), captor.capture()); AvrcpItem item = captor.getValue(); assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS); assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA); assertThat(item.getUuid()).isNotNull(); // Random uuid } @Test public void onPlayPositionChanged() { int songLen = 100; int currSongPos = 33; mService.onPlayPositionChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, songLen, currSongPos); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, songLen, currSongPos); } @Test public void onPlayStatusChanged() { byte status = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK; mService.onPlayStatusChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, PlaybackStateCompat.STATE_REWINDING); } @Test public void onPlayerAppSettingChanged() { byte[] playerAttribRsp = new byte[] {PlayerApplicationSettings.REPEAT_STATUS, PlayerApplicationSettings.JNI_REPEAT_STATUS_ALL_TRACK_REPEAT}; mService.onPlayerAppSettingChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerAttribRsp, 2); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS), any(PlayerApplicationSettings.class)); } @Test public void onAvailablePlayerChanged() { mService.onAvailablePlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY); verify(mStateMachine).sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); } @Test public void handleGetFolderItemsRsp() { int status = 2; AvrcpItem[] items = new AvrcpItem[] {mock(AvrcpItem.class)}; mService.handleGetFolderItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status, items); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS), eq(new ArrayList<>(Arrays.asList(items)))); } @Test public void handleGetPlayerItemsRsp() { AvrcpPlayer[] items = new AvrcpPlayer[] {mock(AvrcpPlayer.class)}; mService.handleGetPlayerItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items); verify(mStateMachine).sendMessage( eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS), eq(new ArrayList<>(Arrays.asList(items)))); } }