diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 6e0a252cca914c95523bd770b71afca639e76b98..fd18ca08423680ef8b77ee3d5cb663663d1f016d 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -187,7 +187,7 @@ class AvrcpControllerStateMachine extends StateMachine {
     }
 
     BrowseTree.BrowseNode findNode(String parentMediaId) {
-        logD("FindNode");
+        logD("findNode(device=" + mDevice + ", mediaId=" + parentMediaId + ")");
         return mBrowseTree.findBrowseNodeByID(parentMediaId);
     }
 
@@ -288,7 +288,8 @@ class AvrcpControllerStateMachine extends StateMachine {
 
     @Override
     protected void unhandledMessage(Message msg) {
-        Log.w(TAG, "Unhandled message in state " + getCurrentState() + "msg.what=" + msg.what);
+        Log.w(TAG, "Unhandled message in state " + getCurrentState() + "msg.what="
+                + eventToString(msg.what));
     }
 
     private static void logD(String message) {
@@ -405,9 +406,8 @@ class AvrcpControllerStateMachine extends StateMachine {
     }
 
     void nowPlayingContentChanged() {
-        mBrowseTree.mNowPlayingNode.setCached(false);
         removeUnusedArtworkFromBrowseTree();
-        sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
+        requestContents(mBrowseTree.mNowPlayingNode);
     }
 
     protected class Disconnected extends State {
@@ -471,7 +471,7 @@ class AvrcpControllerStateMachine extends StateMachine {
 
         @Override
         public boolean processMessage(Message msg) {
-            logD(STATE_TAG + " processMessage " + msg.what);
+            logD(STATE_TAG + " processMessage " + eventToString(msg.what));
             switch (msg.what) {
                 case ACTIVE_DEVICE_CHANGE:
                     int state = msg.arg1;
@@ -636,6 +636,7 @@ class AvrcpControllerStateMachine extends StateMachine {
                     // invalid
                     mBrowseTree.mNowPlayingNode.setCached(false);
                     if (isActive()) {
+                        logD("Addressed player change has invalidated the now playing list");
                         BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
                     }
                     removeUnusedArtworkFromBrowseTree();
@@ -661,13 +662,14 @@ class AvrcpControllerStateMachine extends StateMachine {
                     // guaranteed to have the addressed player now.
                     mAddressedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
 
-                    // Fetch metadata including the now playing list if the new player supports the
-                    // now playing feature
+                    // Fetch metadata including the now playing list. The specification claims that
+                    // the player feature bit only incidates if the player *natively* supports a now
+                    // playing list. However, now playing is mandatory if browsing is supported,
+                    // even if the player doesn't support it. A list of one item can be returned
+                    // instead.
                     mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
                     mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
-                    if (mAddressedPlayer.supportsFeature(AvrcpPlayer.FEATURE_NOW_PLAYING)) {
-                        sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
-                    }
+                    requestContents(mBrowseTree.mNowPlayingNode);
                     logD("AddressedPlayer = " + mAddressedPlayer);
                     return true;
 
@@ -816,29 +818,28 @@ class AvrcpControllerStateMachine extends StateMachine {
 
         @Override
         public void enter() {
-            logD(STATE_TAG + " Entering GetFolderList");
+            logD(STATE_TAG + ": Entering GetFolderList");
             // Setup the timeouts.
             sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
             super.enter();
             mAbort = false;
             Message msg = getCurrentMessage();
             if (msg.what == MESSAGE_GET_FOLDER_ITEMS) {
-                {
-                    logD(STATE_TAG + " new Get Request");
-                    mBrowseNode = (BrowseTree.BrowseNode) msg.obj;
-                }
+                mBrowseNode = (BrowseTree.BrowseNode) msg.obj;
+                logD(STATE_TAG + ": new fetch request, node=" + mBrowseNode);
             }
 
             if (mBrowseNode == null) {
                 transitionTo(mConnected);
             } else {
+                mBrowseNode.setCached(false);
                 navigateToFolderOrRetrieve(mBrowseNode);
             }
         }
 
         @Override
         public boolean processMessage(Message msg) {
-            logD(STATE_TAG + " processMessage " + msg.what);
+            logD(STATE_TAG + " processMessage " + eventToString(msg.what));
             switch (msg.what) {
                 case MESSAGE_PROCESS_GET_FOLDER_ITEMS:
                     ArrayList<AvrcpItem> folderList = (ArrayList<AvrcpItem>) msg.obj;
@@ -941,10 +942,7 @@ class AvrcpControllerStateMachine extends StateMachine {
                             mAddressedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
                             mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
                             mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
-                            mBrowseTree.mNowPlayingNode.setCached(false);
-                            if (mAddressedPlayer.supportsFeature(AvrcpPlayer.FEATURE_NOW_PLAYING)) {
-                                sendMessage(MESSAGE_GET_FOLDER_ITEMS, mBrowseTree.mNowPlayingNode);
-                            }
+                            requestContents(mBrowseTree.mNowPlayingNode);
                         }
                         logD("AddressedPlayer = " + mAddressedPlayer);
 
@@ -979,20 +977,23 @@ class AvrcpControllerStateMachine extends StateMachine {
                     break;
 
                 case MESSAGE_GET_FOLDER_ITEMS:
-                    if (!mBrowseNode.equals(msg.obj)) {
-                        if (shouldAbort(mBrowseNode.getScope(),
-                                ((BrowseTree.BrowseNode) msg.obj).getScope())) {
+                    BrowseTree.BrowseNode requested = (BrowseTree.BrowseNode) msg.obj;
+                    if (!mBrowseNode.equals(requested) || requested.isNowPlaying()) {
+                        if (shouldAbort(mBrowseNode.getScope(), requested.getScope())) {
                             mAbort = true;
                         }
                         deferMessage(msg);
-                        logD("GetFolderItems: Go Get Another Directory");
+                        logD("GetFolderItems: Enqueue new request for node=" + requested
+                                + ", abort=" + mAbort);
                     } else {
-                        logD("GetFolderItems: Get The Same Directory, ignore");
+                        logD("GetFolderItems: Ignore request, node=" + requested);
                     }
                     break;
 
                 default:
                     // All of these messages should be handled by parent state immediately.
+                    logD("GetFolderItems: Passing message to parent state, type="
+                            + eventToString(msg.what));
                     return false;
             }
             return true;
@@ -1094,6 +1095,7 @@ class AvrcpControllerStateMachine extends StateMachine {
 
         @Override
         public void exit() {
+            logd("GetFolderItems: fetch complete, node=" + mBrowseNode);
             removeMessages(MESSAGE_INTERNAL_CMD_TIMEOUT);
             mBrowseNode = null;
             super.exit();
@@ -1328,4 +1330,77 @@ class AvrcpControllerStateMachine extends StateMachine {
         return mService.getResources()
                 .getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus);
     }
+
+    private static String eventToString(int event) {
+        switch (event) {
+            case CONNECT:
+                return "CONNECT";
+            case DISCONNECT:
+                return "DISCONNECT";
+            case ACTIVE_DEVICE_CHANGE:
+                return "ACTIVE_DEVICE_CHANGE";
+            case AUDIO_FOCUS_STATE_CHANGE:
+                return "AUDIO_FOCUS_STATE_CHANGE";
+            case CLEANUP:
+                return "CLEANUP";
+            case CONNECT_TIMEOUT:
+                return "CONNECT_TIMEOUT";
+            case MESSAGE_INTERNAL_ABS_VOL_TIMEOUT:
+                return "MESSAGE_INTERNAL_ABS_VOL_TIMEOUT";
+            case STACK_EVENT:
+                return "STACK_EVENT";
+            case MESSAGE_INTERNAL_CMD_TIMEOUT:
+                return "MESSAGE_INTERNAL_CMD_TIMEOUT";
+            case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
+                return "MESSAGE_PROCESS_SET_ABS_VOL_CMD";
+            case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
+                return "MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION";
+            case MESSAGE_PROCESS_TRACK_CHANGED:
+                return "MESSAGE_PROCESS_TRACK_CHANGED";
+            case MESSAGE_PROCESS_PLAY_POS_CHANGED:
+                return "MESSAGE_PROCESS_PLAY_POS_CHANGED";
+            case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
+                return "MESSAGE_PROCESS_PLAY_STATUS_CHANGED";
+            case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
+                return "MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION";
+            case MESSAGE_PROCESS_GET_FOLDER_ITEMS:
+                return "MESSAGE_PROCESS_GET_FOLDER_ITEMS";
+            case MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE:
+                return "MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE";
+            case MESSAGE_PROCESS_GET_PLAYER_ITEMS:
+                return "MESSAGE_PROCESS_GET_PLAYER_ITEMS";
+            case MESSAGE_PROCESS_FOLDER_PATH:
+                return "MESSAGE_PROCESS_FOLDER_PATH";
+            case MESSAGE_PROCESS_SET_BROWSED_PLAYER:
+                return "MESSAGE_PROCESS_SET_BROWSED_PLAYER";
+            case MESSAGE_PROCESS_SET_ADDRESSED_PLAYER:
+                return "MESSAGE_PROCESS_SET_ADDRESSED_PLAYER";
+            case MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED:
+                return "MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED";
+            case MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED:
+                return "MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED";
+            case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
+                return "MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS";
+            case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS:
+                return "MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS";
+            case MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED:
+                return "MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED";
+            case MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM:
+                return "MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM";
+            case MESSAGE_GET_FOLDER_ITEMS:
+                return "MESSAGE_GET_FOLDER_ITEMS";
+            case MESSAGE_PLAY_ITEM:
+                return "MESSAGE_PLAY_ITEM";
+            case MSG_AVRCP_PASSTHRU:
+                return "MSG_AVRCP_PASSTHRU";
+            case MSG_AVRCP_SET_SHUFFLE:
+                return "MSG_AVRCP_SET_SHUFFLE";
+            case MSG_AVRCP_SET_REPEAT:
+                return "MSG_AVRCP_SET_REPEAT";
+            case MESSAGE_PROCESS_IMAGE_DOWNLOADED:
+                return "MESSAGE_PROCESS_IMAGE_DOWNLOADED";
+            default:
+                return "UNKNOWN_EVENT_ID_" + event;
+        }
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index 4388ac7b1409ed9691b2e9c5455d982f15394669..f6a237d21cda8436ccce20017dcbef369c696f8f 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -325,14 +325,30 @@ public class AvrcpControllerStateMachineTest {
         assertNowPlayingList(nowPlayingList);
     }
 
+    private String avrcpItemListToString(List<AvrcpItem> items) {
+        StringBuilder s = new StringBuilder();
+        s.append("[");
+        if (items != null) {
+            for (int i = 0; i < items.size(); i++) {
+                AvrcpItem item = items.get(i);
+                s.append((item != null ? Long.toString(item.getUid()) : "null"));
+                if (i != items.size() - 1) s.append(", ");
+            }
+        }
+        s.append("]");
+        return s.toString();
+    }
+
     /**
      * Assert that the Now Playing list is a particular value
      */
     private void assertNowPlayingList(List<AvrcpItem> expected) {
         List<AvrcpItem> current = getNowPlayingList();
-        Assert.assertEquals(expected.size(), current.size());
+        String err = "Now playing list incorrect, expected="
+                + avrcpItemListToString(expected) + ", actual=" + avrcpItemListToString(current);
+        Assert.assertEquals(err, expected.size(), current.size());
         for (int i = 0; i < expected.size(); i++) {
-            Assert.assertEquals(expected.get(i), current.get(i));
+            Assert.assertEquals(err, expected.get(i), current.get(i));
         }
     }
 
@@ -690,14 +706,9 @@ public class AvrcpControllerStateMachineTest {
         testPlayers.add(playerOne);
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
                 testPlayers);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         //Verify that the player object is available.
-        mAvrcpStateMachine.requestContents(results);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(1), eq(0));
-        mAvrcpStateMachine.sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
         playerNodes = mAvrcpStateMachine.findNode(results.getID());
         Assert.assertEquals(true, results.isCached());
         Assert.assertEquals("MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}",
@@ -746,10 +757,14 @@ public class AvrcpControllerStateMachineTest {
         setUpConnectedState(true, true);
         final String rootName = "__ROOT__";
 
-        // Set an addressed player that will be in the available players set
+        // Set an addressed player that will be in the available players set. A new player triggers
+        // a now playing list download, so send back nothing.
         mAvrcpStateMachine.sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         clearInvocations(mAvrcpControllerService);
 
         // Send an available players have changed event
@@ -1546,4 +1561,260 @@ public class AvrcpControllerStateMachineTest {
         verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
                 eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
     }
+
+    /**
+     * Test receiving a now playing content changed event while downloading now playing content and
+     * make sure our final now playing content downloaded matches what's expected
+     */
+    @Test
+    public void testNowPlayingListChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted() {
+        setUpConnectedState(true, true);
+        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
+
+        // Fill the list with songs 1 -> 25, more than download step size
+        List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 1; i <= 25; i++) {
+            String title = "Song " + Integer.toString(i);
+            nowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Fill the list with songs 26 -> 50
+        List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 26; i <= 50; i++) {
+            String title = "Song " + Integer.toString(i);
+            updatedNowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Hand hold the download events
+        BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        mAvrcpStateMachine.requestContents(nowPlaying);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Verify download attempt and send some elements over, verify next set is requested
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(20), eq(39));
+
+        // Force a now playing content invalidation and verify attempted download
+        mAvrcpStateMachine.nowPlayingContentChanged();
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Send requested items, they're likely from the new list at this point, but it shouldn't
+        // matter what they are because we should toss them out and restart our download next.
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
+
+        // Wait for the now playing list to be propagated
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Make sure its set by re grabbing the node and checking its contents are cached
+        nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        Assert.assertTrue(nowPlaying.isCached());
+        assertNowPlayingList(updatedNowPlayingList);
+    }
+
+    /**
+     * Test receiving a now playing content changed event right after we queued a fetch of some now
+     * playing items. Make sure our final now playing content downloaded matches what's expected
+     */
+    @Test
+    public void testNowPlayingListChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted() {
+        setUpConnectedState(true, true);
+        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
+
+        // Fill the list with songs 1 -> 25, more than download step size
+        List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 1; i <= 25; i++) {
+            String title = "Song " + Integer.toString(i);
+            nowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Fill the list with songs 26 -> 50
+        List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 26; i <= 50; i++) {
+            String title = "Song " + Integer.toString(i);
+            updatedNowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Hand hold the download events
+        BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        mAvrcpStateMachine.requestContents(nowPlaying);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Verify download attempt and send some elements over, verify next set is requested
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+        mAvrcpStateMachine.nowPlayingContentChanged();
+
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Receiving the previous members should cause our fetch process to realize we're aborted
+        // and a new (second) request should be triggered for the list from the beginning
+        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+
+        // Send whole list
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
+
+        // Wait for the now playing list to be propagated
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Make sure its set by re grabbing the node and checking its contents are cached
+        nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        Assert.assertTrue(nowPlaying.isCached());
+        assertNowPlayingList(updatedNowPlayingList);
+    }
+
+    /**
+     * Test receiving an addressed player changed event while downloading now playing content and
+     * make sure our final now playing content downloaded matches what's expected.
+     */
+    @Test
+    public void testAddressedPlayerChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted() {
+        setUpConnectedState(true, true);
+        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
+
+        // Fill the list with songs 1 -> 25, more than download step size
+        List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 1; i <= 25; i++) {
+            String title = "Song " + Integer.toString(i);
+            nowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Fill the list with songs 26 -> 50
+        List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 26; i <= 50; i++) {
+            String title = "Song " + Integer.toString(i);
+            updatedNowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Hand hold the download events
+        BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        mAvrcpStateMachine.requestContents(nowPlaying);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Verify download attempt and send some elements over, verify next set is requested
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(20), eq(39));
+
+        // Force a now playing content invalidation due to addressed player change
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Send requested items, they're likely from the new list at this point, but it shouldn't
+        // matter what they are because we should toss them out and restart our download next.
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
+        TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
+
+        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
+
+        // Wait for the now playing list to be propagated
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Make sure its set by re grabbing the node and checking its contents are cached
+        nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        Assert.assertTrue(nowPlaying.isCached());
+        assertNowPlayingList(updatedNowPlayingList);
+    }
+
+    /**
+     * Test receiving an addressed player changed event while downloading now playing content and
+     * make sure our final now playing content downloaded matches what's expected.
+     */
+    @Test
+    public void testAddressedPlayerChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted() {
+        setUpConnectedState(true, true);
+        sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
+
+        // Fill the list with songs 1 -> 25, more than download step size
+        List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 1; i <= 25; i++) {
+            String title = "Song " + Integer.toString(i);
+            nowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Fill the list with songs 26 -> 50
+        List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
+        for (int i = 26; i <= 50; i++) {
+            String title = "Song " + Integer.toString(i);
+            updatedNowPlayingList.add(makeNowPlayingItem(i, title));
+        }
+
+        // Hand hold the download events
+        BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        mAvrcpStateMachine.requestContents(nowPlaying);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Verify download attempt and send some elements over, verify next set is requested
+        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+
+        // Force a now playing content invalidation due to addressed player change, happening
+        // before we've received any items from the remote device.
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Now, send the items in and let it process
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
+        TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
+
+        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
+                eq(mTestAddress), eq(0), eq(19));
+
+        // Send requested items, they're likely from the new list at this point, but it shouldn't
+        // matter what they are because we should toss them out and restart our download next.
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
+        mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
+                new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
+        mAvrcpStateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
+
+        // Wait for the now playing list to be propagated
+        TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
+
+        // Make sure its set by re grabbing the node and checking its contents are cached
+        nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
+        Assert.assertTrue(nowPlaying.isCached());
+        assertNowPlayingList(updatedNowPlayingList);
+    }
 }