Loading android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +40 −6 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ class AvrcpControllerStateMachine extends StateMachine { boolean mBrowsingConnected = false; BrowseTree mBrowseTree = null; private AvrcpPlayer mAddressedPlayer = new AvrcpPlayer(); private int mAddressedPlayerId = -1; private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>(); private int mVolumeChangedNotificationsToIgnore = 0; Loading Loading @@ -347,6 +348,20 @@ class AvrcpControllerStateMachine extends StateMachine { } return true; case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED: mAddressedPlayerId = msg.arg1; logD("AddressedPlayer = " + mAddressedPlayerId); AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerId); if (updatedPlayer != null) { mAddressedPlayer = updatedPlayer; logD("AddressedPlayer = " + mAddressedPlayer.getName()); } else { mBrowseTree.mRootNode.setCached(false); mBrowseTree.mRootNode.setExpectedChildren(255); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); } return true; case DISCONNECT: transitionTo(mDisconnecting); return true; Loading Loading @@ -404,11 +419,8 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); } } // Handle the get folder listing action // a) Fetch the listing of folders // b) Once completed return the object listing Loading Loading @@ -525,8 +537,8 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_GET_FOLDER_ITEMS: if (!mBrowseNode.equals(msg.obj)) { if (mBrowseNode.getScope() == ((BrowseTree.BrowseNode) msg.obj).getScope()) { if (shouldAbort(mBrowseNode.getScope(), ((BrowseTree.BrowseNode) msg.obj).getScope())) { mAbort = true; } deferMessage(msg); Loading @@ -545,6 +557,8 @@ class AvrcpControllerStateMachine extends StateMachine { 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: // All of these messages should be handled by parent state immediately. return false; Loading @@ -556,6 +570,23 @@ class AvrcpControllerStateMachine extends StateMachine { return true; } /** * shouldAbort calculates the cases where fetching the current directory is no longer * necessary. * * @return true: a new folder in the same scope * a new player while fetching contents of a folder * false: other cases, specifically Now Playing while fetching a folder */ private boolean shouldAbort(int currentScope, int fetchScope) { if ((currentScope == fetchScope) || (currentScope == AvrcpControllerService.BROWSE_SCOPE_VFS && fetchScope == AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST)) { return true; } return false; } private void fetchContents(BrowseTree.BrowseNode target) { int start = target.getChildrenCount(); int end = Math.min(target.getExpectedChildren(), target.getChildrenCount() Loading Loading @@ -683,18 +714,21 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void onSkipToNext() { logD("onSkipToNext"); onPrepare(); sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD); } @Override public void onSkipToPrevious() { logD("onSkipToPrevious"); onPrepare(); sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD); } @Override public void onSkipToQueueItem(long id) { logD("onSkipToQueueItem" + id); onPrepare(); BrowseTree.BrowseNode node = mBrowseTree.getTrackFromNowPlayingList((int) id); if (node != null) { sendMessage(MESSAGE_PLAY_ITEM, node); Loading Loading @@ -730,10 +764,10 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void onPlayFromMediaId(String mediaId, Bundle extras) { logD("onPlayFromMediaId"); // Play the item if possible. onPrepare(); BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); Log.w(TAG, "Play Node not found"); sendMessage(MESSAGE_PLAY_ITEM, node); } }; Loading android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java +2 −1 Original line number Diff line number Diff line Loading @@ -412,7 +412,8 @@ public class BrowseTree { if (target == null) { return null; } else if (target.equals(mCurrentBrowseNode) || target.equals(mNowPlayingNode)) { || target.equals(mNowPlayingNode) || target.equals(mRootNode)) { return target; } else if (target.isPlayer()) { if (mDepth > 0) { Loading android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -403,6 +403,49 @@ public class AvrcpControllerStateMachineTest { eq(0), eq(4)); } /** * Test addressed media player changed * Verify when the addressed media player changes browsing data updates * Verify that the contents of a player are fetched upon request */ @Test public void testPlayerChanged() { setUpConnectedState(true, true); final String rootName = "__ROOT__"; final String playerName = "Player 1"; //Get the root of the device BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); //Request fetch the list of players BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); mAvrcpStateMachine.requestContents(results); verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), eq(0), eq(19)); //Provide back a player object byte[] playerFeatures = new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0}; AvrcpPlayer playerOne = new AvrcpPlayer(1, playerName, playerFeatures, 1, 1); List<AvrcpPlayer> testPlayers = new ArrayList<>(); testPlayers.add(playerOne); mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers); //Change players and verify that BT attempts to update the results mAvrcpStateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 4); results = mAvrcpStateMachine.findNode(rootName); mAvrcpStateMachine.requestContents(results); verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).getPlayerListNative(eq(mTestAddress), eq(0), eq(19)); } /** * Test that the Now Playing playlist is updated when it changes. */ Loading Loading
android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java +40 −6 Original line number Diff line number Diff line Loading @@ -105,6 +105,7 @@ class AvrcpControllerStateMachine extends StateMachine { boolean mBrowsingConnected = false; BrowseTree mBrowseTree = null; private AvrcpPlayer mAddressedPlayer = new AvrcpPlayer(); private int mAddressedPlayerId = -1; private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>(); private int mVolumeChangedNotificationsToIgnore = 0; Loading Loading @@ -347,6 +348,20 @@ class AvrcpControllerStateMachine extends StateMachine { } return true; case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED: mAddressedPlayerId = msg.arg1; logD("AddressedPlayer = " + mAddressedPlayerId); AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerId); if (updatedPlayer != null) { mAddressedPlayer = updatedPlayer; logD("AddressedPlayer = " + mAddressedPlayer.getName()); } else { mBrowseTree.mRootNode.setCached(false); mBrowseTree.mRootNode.setExpectedChildren(255); BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mRootNode); } return true; case DISCONNECT: transitionTo(mDisconnecting); return true; Loading Loading @@ -404,11 +419,8 @@ class AvrcpControllerStateMachine extends StateMachine { return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND) || (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF); } } // Handle the get folder listing action // a) Fetch the listing of folders // b) Once completed return the object listing Loading Loading @@ -525,8 +537,8 @@ class AvrcpControllerStateMachine extends StateMachine { case MESSAGE_GET_FOLDER_ITEMS: if (!mBrowseNode.equals(msg.obj)) { if (mBrowseNode.getScope() == ((BrowseTree.BrowseNode) msg.obj).getScope()) { if (shouldAbort(mBrowseNode.getScope(), ((BrowseTree.BrowseNode) msg.obj).getScope())) { mAbort = true; } deferMessage(msg); Loading @@ -545,6 +557,8 @@ class AvrcpControllerStateMachine extends StateMachine { 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: // All of these messages should be handled by parent state immediately. return false; Loading @@ -556,6 +570,23 @@ class AvrcpControllerStateMachine extends StateMachine { return true; } /** * shouldAbort calculates the cases where fetching the current directory is no longer * necessary. * * @return true: a new folder in the same scope * a new player while fetching contents of a folder * false: other cases, specifically Now Playing while fetching a folder */ private boolean shouldAbort(int currentScope, int fetchScope) { if ((currentScope == fetchScope) || (currentScope == AvrcpControllerService.BROWSE_SCOPE_VFS && fetchScope == AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST)) { return true; } return false; } private void fetchContents(BrowseTree.BrowseNode target) { int start = target.getChildrenCount(); int end = Math.min(target.getExpectedChildren(), target.getChildrenCount() Loading Loading @@ -683,18 +714,21 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void onSkipToNext() { logD("onSkipToNext"); onPrepare(); sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD); } @Override public void onSkipToPrevious() { logD("onSkipToPrevious"); onPrepare(); sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD); } @Override public void onSkipToQueueItem(long id) { logD("onSkipToQueueItem" + id); onPrepare(); BrowseTree.BrowseNode node = mBrowseTree.getTrackFromNowPlayingList((int) id); if (node != null) { sendMessage(MESSAGE_PLAY_ITEM, node); Loading Loading @@ -730,10 +764,10 @@ class AvrcpControllerStateMachine extends StateMachine { @Override public void onPlayFromMediaId(String mediaId, Bundle extras) { logD("onPlayFromMediaId"); // Play the item if possible. onPrepare(); BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId); Log.w(TAG, "Play Node not found"); sendMessage(MESSAGE_PLAY_ITEM, node); } }; Loading
android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java +2 −1 Original line number Diff line number Diff line Loading @@ -412,7 +412,8 @@ public class BrowseTree { if (target == null) { return null; } else if (target.equals(mCurrentBrowseNode) || target.equals(mNowPlayingNode)) { || target.equals(mNowPlayingNode) || target.equals(mRootNode)) { return target; } else if (target.isPlayer()) { if (mDepth > 0) { Loading
android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java +43 −0 Original line number Diff line number Diff line Loading @@ -403,6 +403,49 @@ public class AvrcpControllerStateMachineTest { eq(0), eq(4)); } /** * Test addressed media player changed * Verify when the addressed media player changes browsing data updates * Verify that the contents of a player are fetched upon request */ @Test public void testPlayerChanged() { setUpConnectedState(true, true); final String rootName = "__ROOT__"; final String playerName = "Player 1"; //Get the root of the device BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); //Request fetch the list of players BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); mAvrcpStateMachine.requestContents(results); verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), eq(0), eq(19)); //Provide back a player object byte[] playerFeatures = new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0}; AvrcpPlayer playerOne = new AvrcpPlayer(1, playerName, playerFeatures, 1, 1); List<AvrcpPlayer> testPlayers = new ArrayList<>(); testPlayers.add(playerOne); mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers); //Change players and verify that BT attempts to update the results mAvrcpStateMachine.sendMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 4); results = mAvrcpStateMachine.findNode(rootName); mAvrcpStateMachine.requestContents(results); verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).getPlayerListNative(eq(mTestAddress), eq(0), eq(19)); } /** * Test that the Now Playing playlist is updated when it changes. */ Loading