Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69cb54e9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove browsing requirement from MediaPlayerWrapper for queue support" into pi-dev

parents a088b40d 79a8e531
Loading
Loading
Loading
Loading
+0 −6
Original line number Original line Diff line number Diff line
@@ -31,12 +31,6 @@ class GPMWrapper extends MediaPlayerWrapper {


    private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata";
    private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata";


    // Google Play Music should always be browsable.
    @Override
    boolean isBrowsable() {
        return true;
    }

    @Override
    @Override
    boolean isMetadataSynced() {
    boolean isMetadataSynced() {
        // Check if currentPlayingQueueId is in the queue
        // Check if currentPlayingQueueId is in the queue
+78 −55
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright 2017 The Android Open Source Project
 * Copyright 2018 The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -29,11 +29,13 @@ import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.util.Log;


import java.util.List;
import java.util.List;
import java.util.Objects;


/*
/*
 * A class to synchronize Media Controller Callbacks and only pass through
 * A class to synchronize Media Controller Callbacks and only pass through
 * an update once all the relevant information is current.
 * an update once all the relevant information is current.
 *
 * TODO (apanicke): Once MediaPlayer2 is supported better, replace this class
 * with that.
 */
 */
class MediaPlayerWrapper {
class MediaPlayerWrapper {
    private static final String TAG = "NewAvrcpMediaPlayerWrapper";
    private static final String TAG = "NewAvrcpMediaPlayerWrapper";
@@ -44,7 +46,6 @@ class MediaPlayerWrapper {
    private String mPackageName;
    private String mPackageName;
    private Looper mLooper;
    private Looper mLooper;


    private boolean mIsBrowsable = false;
    private MediaData mCurrentData;
    private MediaData mCurrentData;


    @GuardedBy("mCallbackLock")
    @GuardedBy("mCallbackLock")
@@ -57,45 +58,26 @@ class MediaPlayerWrapper {
        mCurrentData = new MediaData(null, null, null);
        mCurrentData = new MediaData(null, null, null);
    }
    }


    interface Callback {
    public interface Callback {
        void mediaUpdatedCallback(MediaData data);
        void mediaUpdatedCallback(MediaData data);
    }
    }


    class MediaData {
    boolean isReady() {
        public List<MediaSession.QueueItem> queue;
        if (getPlaybackState() == null) {
        public PlaybackState state;
            d("isReady(): PlaybackState is null");
        public MediaMetadata metadata;

        MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) {
            metadata = m;
            state = s;
            queue = q;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null) return false;
            if (!(o instanceof MediaData)) return false;

            final MediaData u = (MediaData) o;

            if (!Objects.equals(metadata, u.metadata)) {
                return false;
            }

            if (!Objects.equals(queue, u.queue)) {
            return false;
            return false;
        }
        }


            if (!playstateEquals(state, u.state)) {
        if (getMetadata() == null) {
            d("isReady(): Metadata is null");
            return false;
            return false;
        }
        }


        return true;
        return true;
    }
    }
    }


    static MediaPlayerWrapper wrap(MediaController controller, Looper looper, boolean browsable) {
    // TODO (apanicke): Implement a factory to make testing and creating interop wrappers easier
    static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
        if (controller == null || looper == null) {
        if (controller == null || looper == null) {
            e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller
            e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller
                    + " | Looper: " + looper);
                    + " | Looper: " + looper);
@@ -113,12 +95,10 @@ class MediaPlayerWrapper {
        newWrapper.mMediaController = controller;
        newWrapper.mMediaController = controller;
        newWrapper.mPackageName = controller.getPackageName();
        newWrapper.mPackageName = controller.getPackageName();
        newWrapper.mLooper = looper;
        newWrapper.mLooper = looper;
        newWrapper.mIsBrowsable = browsable;


        newWrapper.mCurrentData.queue = newWrapper.getQueue();
        newWrapper.mCurrentData.queue = newWrapper.getQueue();
        newWrapper.mCurrentData.metadata = newWrapper.getMetadata();
        newWrapper.mCurrentData.metadata = newWrapper.getMetadata();
        newWrapper.mCurrentData.state = newWrapper.getPlaybackState();
        newWrapper.mCurrentData.state = newWrapper.getPlaybackState();

        return newWrapper;
        return newWrapper;
    }
    }


@@ -129,17 +109,11 @@ class MediaPlayerWrapper {
        mLooper = null;
        mLooper = null;
    }
    }


    boolean isBrowsable() {
        return mIsBrowsable;
    }

    String getPackageName() {
    String getPackageName() {
        return mPackageName;
        return mPackageName;
    }
    }


    List<MediaSession.QueueItem> getQueue() {
    List<MediaSession.QueueItem> getQueue() {
        if (!isBrowsable()) return null;

        return mMediaController.getQueue();
        return mMediaController.getQueue();
    }
    }


@@ -156,7 +130,23 @@ class MediaPlayerWrapper {
        return mMediaController.getPlaybackState();
        return mMediaController.getPlaybackState();
    }
    }


    // TODO: Implement shuffle and repeat support. Right now these use custom actions
    MediaData getCurrentMediaData() {
        return mCurrentData;
    }

    void playItemFromQueue(long qid) {
        // Return immediately if no queue exists.
        if (getQueue() == null) {
            Log.w(TAG, "playItemFromQueue: Trying to play item for player that has no queue: "
                    + mPackageName);
            return;
        }

        MediaController.TransportControls controller = mMediaController.getTransportControls();
        controller.skipToQueueItem(qid);
    }

    // TODO (apanicke): Implement shuffle and repeat support. Right now these use custom actions
    // and it may only be possible to do this with Google Play Music
    // and it may only be possible to do this with Google Play Music
    boolean isShuffleSupported() {
    boolean isShuffleSupported() {
        return false;
        return false;
@@ -175,12 +165,10 @@ class MediaPlayerWrapper {
    }
    }


    /**
    /**
     * Return whether the queue, metadata, and queueID are all in sync. If
     * Return whether the queue, metadata, and queueID are all in sync.
     * browsing isn't supported we don't have to worry about the queue as
     * the queue doesn't exist
     */
     */
    boolean isMetadataSynced() {
    boolean isMetadataSynced() {
        if (isBrowsable()) {
        if (getQueue() != null) {
            // Check if currentPlayingQueueId is in the current Queue
            // Check if currentPlayingQueueId is in the current Queue
            MediaSession.QueueItem currItem = null;
            MediaSession.QueueItem currItem = null;


@@ -237,6 +225,21 @@ class MediaPlayerWrapper {
        mControllerCallbacks = null;
        mControllerCallbacks = null;
    }
    }


    void updateMediaController(MediaController newController) {
        if (newController == mMediaController) return;

        synchronized (mCallbackLock) {
            if (mRegisteredCallback == null || mControllerCallbacks == null) {
                return;
            }
        }

        mControllerCallbacks.cleanup();
        mMediaController = newController;
        mControllerCallbacks = new MediaControllerListener(mLooper);
        d("Controller for " + mPackageName + " was updated.");
    }

    class TimeoutHandler extends Handler {
    class TimeoutHandler extends Handler {
        private static final int MSG_TIMEOUT = 0;
        private static final int MSG_TIMEOUT = 0;
        private static final long CALLBACK_TIMEOUT_MS = 1000;
        private static final long CALLBACK_TIMEOUT_MS = 1000;
@@ -255,7 +258,7 @@ class MediaPlayerWrapper {
            Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName);
            Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName);
            Log.e(TAG, "  └ Current Metadata: " + getMetadata().getDescription());
            Log.e(TAG, "  └ Current Metadata: " + getMetadata().getDescription());
            Log.e(TAG, "  └ Current Playstate: " + getPlaybackState());
            Log.e(TAG, "  └ Current Playstate: " + getPlaybackState());
            for (int i = 0; i < getQueue().size(); i++) {
            for (int i = 0; getQueue() != null && i < getQueue().size(); i++) {
                Log.e(TAG, "  └ QueueItem(" + i + "): " + getQueue().get(i));
                Log.e(TAG, "  └ QueueItem(" + i + "): " + getQueue().get(i));
            }
            }


@@ -293,10 +296,7 @@ class MediaPlayerWrapper {
                mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT);
                mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT);


                if (!isMetadataSynced()) {
                if (!isMetadataSynced()) {
                    if (DEBUG) {
                    d("trySendMediaUpdate(): Starting media update timeout");
                        Log.d(TAG, "trySendMediaUpdate(): " + mPackageName
                                + ": Starting media update timeout");
                    }
                    mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT,
                    mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT,
                            TimeoutHandler.CALLBACK_TIMEOUT_MS);
                            TimeoutHandler.CALLBACK_TIMEOUT_MS);
                    return;
                    return;
@@ -328,6 +328,11 @@ class MediaPlayerWrapper {


        @Override
        @Override
        public void onMetadataChanged(MediaMetadata metadata) {
        public void onMetadataChanged(MediaMetadata metadata) {
            if (!isReady()) {
                Log.v(TAG, mPackageName + " tried to update with incomplete metadata");
                return;
            }

            Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription());
            Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription());


            if (!metadata.equals(getMetadata())) {
            if (!metadata.equals(getMetadata())) {
@@ -353,6 +358,11 @@ class MediaPlayerWrapper {


        @Override
        @Override
        public void onPlaybackStateChanged(PlaybackState state) {
        public void onPlaybackStateChanged(PlaybackState state) {
            if (!isReady()) {
                Log.v(TAG, mPackageName + " tried to update with no state");
                return;
            }

            Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString());
            Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString());


            if (!playstateEquals(state, getPlaybackState())) {
            if (!playstateEquals(state, getPlaybackState())) {
@@ -377,10 +387,12 @@ class MediaPlayerWrapper {
        @Override
        @Override
        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
            Log.v(TAG, "onQueueChanged(): " + mPackageName);
            Log.v(TAG, "onQueueChanged(): " + mPackageName);
            if (!isBrowsable()) {

                e("Queue changed for non-browsable player " + mPackageName);
            if (!isReady()) {
                Log.v(TAG, mPackageName + " tried to updated with no queue");
                return;
                return;
            }
            }

            if (!queue.equals(getQueue())) {
            if (!queue.equals(getQueue())) {
                e("The callback queue isn't the current queue");
                e("The callback queue isn't the current queue");
            }
            }
@@ -400,7 +412,9 @@ class MediaPlayerWrapper {
        }
        }


        @Override
        @Override
        public void onSessionDestroyed() {}
        public void onSessionDestroyed() {
            Log.w(TAG, "The session was destroyed " + mPackageName);
        }


        @VisibleForTesting
        @VisibleForTesting
        Handler getTimeoutHandler() {
        Handler getTimeoutHandler() {
@@ -430,6 +444,7 @@ class MediaPlayerWrapper {
        return false;
        return false;
    }
    }


    // TODO: Use this function when returning the now playing list
    /**
    /**
     * Extracts different pieces of metadata from a MediaSession.QueueItem
     * Extracts different pieces of metadata from a MediaSession.QueueItem
     * and builds a MediaMetadata Object out of it.
     * and builds a MediaMetadata Object out of it.
@@ -475,9 +490,17 @@ class MediaPlayerWrapper {
        }
        }
    }
    }


    private void d(String message) {
        if (DEBUG) Log.d(TAG, mPackageName + ": " + message);
    }

    @VisibleForTesting
    @VisibleForTesting
    Handler getTimeoutHandler() {
    Handler getTimeoutHandler() {
        if (mControllerCallbacks == null) return null;
        if (mControllerCallbacks == null) return null;
        return mControllerCallbacks.getTimeoutHandler();
        return mControllerCallbacks.getTimeoutHandler();
    }
    }

    public void dump(StringBuilder sb) {
        sb.append(mMediaController.toString() + "\n");
    }
}
}
+61 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.bluetooth.avrcp;

import android.media.MediaMetadata;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;

import java.util.List;
import java.util.Objects;

/*
 * Helper class to transport metadata around AVRCP
 */
class MediaData {
    public List<MediaSession.QueueItem> queue;
    public PlaybackState state;
    public MediaMetadata metadata;

    MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) {
        metadata = m;
        state = s;
        queue = q;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof MediaData)) return false;

        final MediaData u = (MediaData) o;

        if (!MediaPlayerWrapper.playstateEquals(state, u.state)) {
            return false;
        }

        if (!Objects.equals(metadata, u.metadata)) {
            return false;
        }

        if (!Objects.equals(queue, u.queue)) {
            return false;
        }

        return true;
    }
}
+58 −61
Original line number Original line Diff line number Diff line
@@ -54,7 +54,7 @@ public class MediaPlayerWrapperTest {
    private PlaybackState.Builder mTestState;
    private PlaybackState.Builder mTestState;


    @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs;
    @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs;
    @Captor ArgumentCaptor<MediaPlayerWrapper.MediaData> mMediaUpdateData;
    @Captor ArgumentCaptor<MediaData> mMediaUpdateData;
    @Mock Log.TerribleFailureHandler mFailHandler;
    @Mock Log.TerribleFailureHandler mFailHandler;
    @Mock MediaController mMockController;
    @Mock MediaController mMockController;
    @Mock MediaPlayerWrapper.Callback mTestCbs;
    @Mock MediaPlayerWrapper.Callback mTestCbs;
@@ -76,7 +76,7 @@ public class MediaPlayerWrapperTest {
    public void setUp() {
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);


        // Set failure handler to caputer Log.wtf messages
        // Set failure handler to capture Log.wtf messages
        Log.setWtfHandler(mFailHandler);
        Log.setWtfHandler(mFailHandler);


        // Set up Looper thread for the timeout handler
        // Set up Looper thread for the timeout handler
@@ -127,7 +127,7 @@ public class MediaPlayerWrapperTest {
        doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue();
        doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue();


        // Enable testing flag which enables Log.wtf statements. Some tests test against improper
        // Enable testing flag which enables Log.wtf statements. Some tests test against improper
        // behaviour and the TerribleFailureListener is a good way to ensure that the error occured
        // behaviour and the TerribleFailureListener is a good way to ensure that the error occurred
        MediaPlayerWrapper.sTesting = true;
        MediaPlayerWrapper.sTesting = true;
    }
    }


@@ -137,26 +137,55 @@ public class MediaPlayerWrapperTest {
     */
     */
    @Test
    @Test
    public void testNullControllerLooper() {
    public void testNullControllerLooper() {
        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper(), false);
        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper());
        Assert.assertNull(wrapper);
        Assert.assertNull(wrapper);


        wrapper = MediaPlayerWrapper.wrap(mMockController, null, false);
        wrapper = MediaPlayerWrapper.wrap(mMockController, null);
        Assert.assertNull(wrapper);
        Assert.assertNull(wrapper);
    }
    }


    /*
     * Test to make sure that isReady() returns false if there is no playback state,
     * there is no metadata, or if the metadata has no title.
     */
    @Test
    public void testIsReady() {
        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        Assert.assertTrue(wrapper.isReady());

        // Test isReady() is false when the playback state is null
        doReturn(null).when(mMockController).getPlaybackState();
        Assert.assertFalse(wrapper.isReady());

        // Restore the old playback state
        doReturn(mTestState.build()).when(mMockController).getPlaybackState();
        Assert.assertTrue(wrapper.isReady());

        // Test isReady() is false when the metadata is null
        doReturn(null).when(mMockController).getMetadata();
        Assert.assertFalse(wrapper.isReady());

        // Restore the old metadata
        doReturn(mTestMetadata.build()).when(mMockController).getMetadata();
        Assert.assertTrue(wrapper.isReady());
    }

    /*
    /*
     * Test to make sure that a media player update gets sent whenever a Media metadata or playback
     * Test to make sure that a media player update gets sent whenever a Media metadata or playback
     * state change occurs instead of waiting for all data to be synced if the player doesn't
     * state change occurs instead of waiting for all data to be synced if the player doesn't
     * support browsing and queues.
     * support queues.
     */
     */
    @Test
    @Test
    public void testNoBrowsingMediaUpdates() {
    public void testNoQueueMediaUpdates() {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Return null when getting the queue
        doReturn(null).when(mMockController).getQueue();

        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
@@ -168,7 +197,7 @@ public class MediaPlayerWrapperTest {


        // Assert that the metadata was updated and playback state wasn't
        // Assert that the metadata was updated and playback state wasn't
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue();
        MediaData data = mMediaUpdateData.getValue();
        Assert.assertEquals(
        Assert.assertEquals(
                "Returned Metadata isn't equal to given Metadata",
                "Returned Metadata isn't equal to given Metadata",
                data.metadata.getDescription(),
                data.metadata.getDescription(),
@@ -202,58 +231,23 @@ public class MediaPlayerWrapperTest {
        verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
        verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
    }
    }


    /*
     * Test that trying to get the queue on a player that doesn't support
     * browsing returns false.
     */
    @Test
    public void testNoBrowsingNullQueue() {
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
        wrapper.registerCallback(mTestCbs);

        // Grab the callbacks the wrapper registered with the controller
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();

        // Update Queue returned by controller
        mTestQueue.add(
                new MediaDescription.Builder()
                        .setTitle("New Title")
                        .setSubtitle("BT Test Artist")
                        .setDescription("BT Test Album")
                        .setMediaId("103"));
        doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue();
        controllerCallbacks.onQueueChanged(getQueueFromDescriptions(mTestQueue));

        // Verify no updates happened
        verify(mTestCbs, never()).mediaUpdatedCallback(any());

        // Verify that getQueue() returns null
        Assert.assertNull(wrapper.getQueue());

        // Verify that there was an error message pending and there were no timeouts
        Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT));
        verify(mFailHandler, times(1)).onTerribleFailure(any(), any(), anyBoolean());
    }

    /*
    /*
     * This test updates the metadata and playback state returned by the
     * This test updates the metadata and playback state returned by the
     * controller then sends an update. This is to make sure that all relevant
     * controller then sends an update. This is to make sure that all relevant
     * information is sent with every update. In the case without browsing,
     * information is sent with every update. In the case without a queue,
     * metadata and playback state are updated.
     * metadata and playback state are updated.
     */
     */

    @Test
    @Test
    public void testAllDataOnUpdate() {
    public void testDataOnUpdateNoQueue() {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Return null when getting the queue
        doReturn(null).when(mMockController).getQueue();

        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
@@ -271,7 +265,7 @@ public class MediaPlayerWrapperTest {


        // Assert that both metadata and playback state are there.
        // Assert that both metadata and playback state are there.
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue();
        MediaData data = mMediaUpdateData.getValue();
        Assert.assertEquals(
        Assert.assertEquals(
                "Returned PlaybackState isn't equal to given PlaybackState",
                "Returned PlaybackState isn't equal to given PlaybackState",
                data.state.toString(),
                data.state.toString(),
@@ -288,7 +282,7 @@ public class MediaPlayerWrapperTest {
    }
    }


    /*
    /*
     * This test sends repeted Playback State updates that only have a short
     * This test sends repeated Playback State updates that only have a short
     * position update change to see if they get debounced.
     * position update change to see if they get debounced.
     */
     */
    @Test
    @Test
@@ -296,9 +290,12 @@ public class MediaPlayerWrapperTest {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Return null when getting the queue
        doReturn(null).when(mMockController).getQueue();

        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
@@ -310,7 +307,7 @@ public class MediaPlayerWrapperTest {


        // Assert that both metadata and only the first playback state is there.
        // Assert that both metadata and only the first playback state is there.
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue();
        MediaData data = mMediaUpdateData.getValue();
        Assert.assertEquals(
        Assert.assertEquals(
                "Returned PlaybackState isn't equal to given PlaybackState",
                "Returned PlaybackState isn't equal to given PlaybackState",
                data.state.toString(),
                data.state.toString(),
@@ -350,7 +347,7 @@ public class MediaPlayerWrapperTest {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Cleanup the wrapper
        // Cleanup the wrapper
@@ -370,7 +367,7 @@ public class MediaPlayerWrapperTest {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
@@ -400,7 +397,7 @@ public class MediaPlayerWrapperTest {
        // Create the wrapper object and register the looper with the timeout handler
        // Create the wrapper object and register the looper with the timeout handler
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
@@ -430,7 +427,7 @@ public class MediaPlayerWrapperTest {
        // Assert that the callback was called with the updated data
        // Assert that the callback was called with the updated data
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
        verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
        verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
        MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue();
        MediaData data = mMediaUpdateData.getValue();
        Assert.assertEquals(
        Assert.assertEquals(
                "Returned Metadata isn't equal to given Metadata",
                "Returned Metadata isn't equal to given Metadata",
                data.metadata.getDescription(),
                data.metadata.getDescription(),
@@ -460,7 +457,7 @@ public class MediaPlayerWrapperTest {
                InstrumentationRegistry.getInstrumentation()
                InstrumentationRegistry.getInstrumentation()
                        .acquireLooperManager(mThread.getLooper());
                        .acquireLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
@@ -494,7 +491,7 @@ public class MediaPlayerWrapperTest {
                InstrumentationRegistry.getInstrumentation()
                InstrumentationRegistry.getInstrumentation()
                        .acquireLooperManager(mThread.getLooper());
                        .acquireLooperManager(mThread.getLooper());
        MediaPlayerWrapper wrapper =
        MediaPlayerWrapper wrapper =
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true);
                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
        wrapper.registerCallback(mTestCbs);
        wrapper.registerCallback(mTestCbs);


        // Grab the callbacks the wrapper registered with the controller
        // Grab the callbacks the wrapper registered with the controller
@@ -545,7 +542,7 @@ public class MediaPlayerWrapperTest {
            // Check that the callback was called a certain number of times and
            // Check that the callback was called a certain number of times and
            // that all the Media info matches what was given
            // that all the Media info matches what was given
            verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture());
            verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture());
            MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue();
            MediaData data = mMediaUpdateData.getValue();
            Assert.assertEquals(
            Assert.assertEquals(
                    "Returned Metadata isn't equal to given Metadata",
                    "Returned Metadata isn't equal to given Metadata",
                    data.metadata.getDescription(),
                    data.metadata.getDescription(),