Merge "DO NOT MERGE: Make NIAP a Global Setting toggle." into qt-qpr1-dev
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index e0df8b2..cc8f88d 100644
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -596,7 +596,7 @@
 
     boolean isConnected() {
         synchronized (this) {
-            return (getCurrentState() == mConnected);
+            return (getConnectionState() == BluetoothProfile.STATE_CONNECTED);
         }
     }
 
diff --git a/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java b/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
index b5886c2..3ce2c5d 100644
--- a/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
+++ b/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java
@@ -23,6 +23,9 @@
 import android.os.Message;
 import android.util.Log;
 
+import com.android.bluetooth.Utils;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -47,6 +50,7 @@
     private static final int MSG_CONNECT_CB = 1;
     private static final int MSG_TIMEOUT = 2;
 
+    private static BrowsablePlayerConnector sInjectConnector;
     private Handler mHandler;
     private Context mContext;
     private PlayerListCallback mCallback;
@@ -58,11 +62,19 @@
         void run(List<BrowsedPlayerWrapper> result);
     }
 
+    private static void setInstanceForTesting(BrowsablePlayerConnector connector) {
+        Utils.enforceInstrumentationTestMode();
+        sInjectConnector = connector;
+    }
+
     static BrowsablePlayerConnector connectToPlayers(
             Context context,
             Looper looper,
             List<ResolveInfo> players,
             PlayerListCallback cb) {
+        if (sInjectConnector != null) {
+            return sInjectConnector;
+        }
         if (cb == null) {
             Log.wtfStack(TAG, "Null callback passed");
             return null;
diff --git a/src/com/android/bluetooth/avrcp/GPMWrapper.java b/src/com/android/bluetooth/avrcp/GPMWrapper.java
index 87e5ec0..ea9875d 100644
--- a/src/com/android/bluetooth/avrcp/GPMWrapper.java
+++ b/src/com/android/bluetooth/avrcp/GPMWrapper.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.avrcp;
 
 import android.media.session.MediaSession;
+import android.os.Looper;
 import android.util.Log;
 
 /**
@@ -28,6 +29,10 @@
     private static final String TAG = "AvrcpGPMWrapper";
     private static final boolean DEBUG = true;
 
+    GPMWrapper(MediaController controller, Looper looper) {
+        super(controller, looper);
+    }
+
     @Override
     boolean isMetadataSynced() {
         if (getQueue() == null) {
diff --git a/src/com/android/bluetooth/avrcp/MediaPlayerList.java b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
index 29f4cf9..5756121 100644
--- a/src/com/android/bluetooth/avrcp/MediaPlayerList.java
+++ b/src/com/android/bluetooth/avrcp/MediaPlayerList.java
@@ -24,6 +24,9 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
@@ -33,6 +36,7 @@
 import android.view.KeyEvent;
 
 import com.android.bluetooth.Utils;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -79,6 +83,8 @@
     private Looper mLooper; // Thread all media player callbacks and timeouts happen on
     private PackageManager mPackageManager;
     private MediaSessionManager mMediaSessionManager;
+    private MediaData mCurrMediaData = null;
+    private final AudioManager mAudioManager;
 
     private Map<Integer, MediaPlayerWrapper> mMediaPlayers =
             Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>());
@@ -88,6 +94,9 @@
             Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>());
     private int mActivePlayerId = NO_ACTIVE_PLAYER;
 
+    @VisibleForTesting
+    private boolean mAudioPlaybackIsActive = false;
+
     private AvrcpTargetService.ListCallback mCallback;
     private BrowsablePlayerConnector mBrowsablePlayerConnector;
 
@@ -122,6 +131,9 @@
         pkgFilter.addDataScheme(PACKAGE_SCHEME);
         context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter);
 
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mAudioManager.registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(mLooper));
+
         mMediaSessionManager =
                 (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
         mMediaSessionManager.addOnActiveSessionsChangedListener(
@@ -190,6 +202,8 @@
         mMediaSessionManager.setCallback(null, null);
         mMediaSessionManager = null;
 
+        mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback);
+
         mMediaPlayerIds.clear();
 
         for (MediaPlayerWrapper player : mMediaPlayers.values()) {
@@ -275,7 +289,16 @@
         final MediaPlayerWrapper player = getActivePlayer();
         if (player == null) return null;
 
-        return player.getPlaybackState();
+        PlaybackState state = player.getPlaybackState();
+        if (mAudioPlaybackIsActive
+                && (state == null || state.getState() != PlaybackState.STATE_PLAYING)) {
+            return new PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PLAYING,
+                          state == null ? 0 : state.getPosition(),
+                          1.0f)
+                .build();
+        }
+        return state;
     }
 
     @NonNull
@@ -406,12 +429,8 @@
         }
     }
 
-    // Adds the controller to the MediaPlayerList or updates the controller if we already had
-    // a controller for a package. Returns the new ID of the controller where its added or its
-    // previous value if it already existed. Returns -1 if the controller passed in is invalid
-    int addMediaPlayer(android.media.session.MediaController controller) {
-        if (controller == null) return -1;
-
+    @VisibleForTesting
+    int addMediaPlayer(MediaController controller) {
         // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that
         // there is no active player. If we already have a browsable player for the package, reuse
         // that key.
@@ -427,7 +446,7 @@
         if (mMediaPlayers.containsKey(playerId)) {
             d("Already have a controller for the player: " + packageName + ", updating instead");
             MediaPlayerWrapper player = mMediaPlayers.get(playerId);
-            player.updateMediaController(MediaControllerFactory.wrap(controller));
+            player.updateMediaController(controller);
 
             // If the media controller we updated was the active player check if the media updated
             if (playerId == mActivePlayerId) {
@@ -437,8 +456,8 @@
             return playerId;
         }
 
-        MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap(
-                MediaControllerFactory.wrap(controller),
+        MediaPlayerWrapper newPlayer = MediaPlayerWrapperFactory.wrap(
+                controller,
                 mLooper);
 
         Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: "
@@ -448,6 +467,18 @@
         return playerId;
     }
 
+    // Adds the controller to the MediaPlayerList or updates the controller if we already had
+    // a controller for a package. Returns the new ID of the controller where its added or its
+    // previous value if it already existed. Returns -1 if the controller passed in is invalid
+    int addMediaPlayer(android.media.session.MediaController controller) {
+        if (controller == null) {
+            e("Trying to add a null MediaController");
+            return -1;
+        }
+
+        return addMediaPlayer(MediaControllerFactory.wrap(controller));
+    }
+
     void removeMediaPlayer(int playerId) {
         if (!mMediaPlayers.containsKey(playerId)) {
             e("Trying to remove nonexistent media player: " + playerId);
@@ -495,7 +526,12 @@
             sendFolderUpdate(true, true, false);
         }
 
-        sendMediaUpdate(getActivePlayer().getCurrentMediaData());
+        MediaData data = getActivePlayer().getCurrentMediaData();
+        if (mAudioPlaybackIsActive) {
+            data.state = mCurrMediaData.state;
+            Log.d(TAG, "setActivePlayer mAudioPlaybackIsActive=true, state=" + data.state);
+        }
+        sendMediaUpdate(data);
     }
 
     // TODO (apanicke): Add logging for media key events in dumpsys
@@ -528,6 +564,8 @@
             data.queue.add(data.metadata);
         }
 
+        Log.d(TAG, "sendMediaUpdate state=" + data.state);
+        mCurrMediaData = data;
         mCallback.run(data);
     }
 
@@ -591,6 +629,78 @@
         }
     };
 
+    void updateMediaForAudioPlayback() {
+        MediaData currMediaData = null;
+        PlaybackState currState = null;
+        if (getActivePlayer() == null) {
+            Log.d(TAG, "updateMediaForAudioPlayback: no active player");
+            PlaybackState.Builder builder = new PlaybackState.Builder()
+                    .setState(PlaybackState.STATE_STOPPED, 0L, 0L);
+            List<Metadata> queue = new ArrayList<Metadata>();
+            queue.add(Util.empty_data());
+            currMediaData = new MediaData(
+                    Util.empty_data(),
+                    builder.build(),
+                    queue
+                );
+        } else {
+            currMediaData = getActivePlayer().getCurrentMediaData();
+            currState = currMediaData.state;
+        }
+
+        if (currState != null
+                && currState.getState() == PlaybackState.STATE_PLAYING) {
+            Log.i(TAG, "updateMediaForAudioPlayback: Active player is playing, drop it");
+            return;
+        }
+
+        if (mAudioPlaybackIsActive) {
+            PlaybackState.Builder builder = new PlaybackState.Builder()
+                    .setState(PlaybackState.STATE_PLAYING,
+                        currState == null ? 0 : currState.getPosition(),
+                        1.0f);
+            currMediaData.state = builder.build();
+        }
+        Log.i(TAG, "updateMediaForAudioPlayback: update state=" + currMediaData.state);
+        sendMediaUpdate(currMediaData);
+    }
+
+    @VisibleForTesting
+    void injectAudioPlaybacActive(boolean isActive) {
+        mAudioPlaybackIsActive = isActive;
+        updateMediaForAudioPlayback();
+    }
+
+    private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback =
+            new AudioManager.AudioPlaybackCallback() {
+        @Override
+        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
+            if (configs == null) {
+                return;
+            }
+            boolean isActive = false;
+            Log.v(TAG, "onPlaybackConfigChanged(): Configs list size=" + configs.size());
+            for (AudioPlaybackConfiguration config : configs) {
+                if (config.isActive() && (config.getAudioAttributes().getUsage()
+                            == AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
+                        && (config.getAudioAttributes().getContentType()
+                            == AudioAttributes.CONTENT_TYPE_SPEECH)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onPlaybackConfigChanged(): config="
+                                 + AudioPlaybackConfiguration.toLogFriendlyString(config));
+                    }
+                    isActive = true;
+                }
+            }
+            if (isActive != mAudioPlaybackIsActive) {
+                Log.d(TAG, "onPlaybackConfigChanged isActive=" + isActive
+                        + ", mAudioPlaybackIsActive=" + mAudioPlaybackIsActive);
+                mAudioPlaybackIsActive = isActive;
+                updateMediaForAudioPlayback();
+            }
+        }
+    };
+
     private final MediaPlayerWrapper.Callback mMediaPlayerCallback =
             new MediaPlayerWrapper.Callback() {
         @Override
@@ -605,6 +715,10 @@
                 return;
             }
 
+            if (mAudioPlaybackIsActive && (data.state.getState() != PlaybackState.STATE_PLAYING)) {
+                Log.d(TAG, "Some audio playbacks are still active, drop it");
+                return;
+            }
             sendMediaUpdate(data);
         }
     };
diff --git a/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java b/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
index e4176b3..9eba036 100644
--- a/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
+++ b/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java
@@ -54,10 +54,6 @@
     private final Object mCallbackLock = new Object();
     private Callback mRegisteredCallback = null;
 
-    protected MediaPlayerWrapper() {
-        mCurrentData = new MediaData(null, null, null);
-    }
-
     public interface Callback {
         void mediaUpdatedCallback(MediaData data);
     }
@@ -80,30 +76,15 @@
         return true;
     }
 
-    // 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) {
-            e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller
-                    + " | Looper: " + looper);
-            return null;
-        }
+    MediaPlayerWrapper(MediaController controller, Looper looper) {
+        mMediaController = controller;
+        mPackageName = controller.getPackageName();
+        mLooper = looper;
 
-        MediaPlayerWrapper newWrapper;
-        if (controller.getPackageName().equals("com.google.android.music")) {
-            Log.v(TAG, "Creating compatibility wrapper for Google Play Music");
-            newWrapper = new GPMWrapper();
-        } else {
-            newWrapper = new MediaPlayerWrapper();
-        }
-
-        newWrapper.mMediaController = controller;
-        newWrapper.mPackageName = controller.getPackageName();
-        newWrapper.mLooper = looper;
-
-        newWrapper.mCurrentData.queue = Util.toMetadataList(newWrapper.getQueue());
-        newWrapper.mCurrentData.metadata = Util.toMetadata(newWrapper.getMetadata());
-        newWrapper.mCurrentData.state = newWrapper.getPlaybackState();
-        return newWrapper;
+        mCurrentData = new MediaData(null, null, null);
+        mCurrentData.queue = Util.toMetadataList(getQueue());
+        mCurrentData.metadata = Util.toMetadata(getMetadata());
+        mCurrentData.state = getPlaybackState();
     }
 
     void cleanup() {
diff --git a/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java b/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java
new file mode 100644
index 0000000..d3ef751
--- /dev/null
+++ b/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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.os.Looper;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Provide a method to inject custom MediaControllerWrapper objects for testing. By using the
+ * factory methods instead of calling the wrap method of MediaControllerWrapper directly, we can
+ * inject a custom MediaControllerWrapper that can be used with JUnit and Mockito to set
+ * expectations and validate behaviour in tests.
+ */
+public final class MediaPlayerWrapperFactory {
+    private static MediaPlayerWrapper sInjectedWrapper;
+
+    static MediaPlayerWrapper wrap(MediaController controller, Looper looper) {
+        if (sInjectedWrapper != null) return sInjectedWrapper;
+        if (controller == null || looper == null) {
+            return null;
+        }
+
+        MediaPlayerWrapper newWrapper;
+        if (controller.getPackageName().equals("com.google.android.music")) {
+            newWrapper = new GPMWrapper(controller, looper);
+        } else {
+            newWrapper = new MediaPlayerWrapper(controller, looper);
+        }
+        return newWrapper;
+    }
+
+    @VisibleForTesting
+    static void inject(MediaPlayerWrapper wrapper) {
+        sInjectedWrapper = wrapper;
+    }
+}
+
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index 5ef481a..009e42c 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -2314,6 +2314,8 @@
     }
 
     boolean setPhonebookAccessPermission(BluetoothDevice device, int value) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH PRIVILEGED permission");
         SharedPreferences pref = getSharedPreferences(PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE,
                 Context.MODE_PRIVATE);
         SharedPreferences.Editor editor = pref.edit();
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index c5485f9..989043a 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -841,8 +841,6 @@
                     break;
                 }
                 case CALL_STATE_CHANGED: {
-                    if (mDeviceSilenced) break;
-
                     HeadsetCallState callState = (HeadsetCallState) message.obj;
                     if (!mNativeInterface.phoneStateChange(mDevice, callState)) {
                         stateLogW("processCallState: failed to update call state " + callState);
diff --git a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java
new file mode 100644
index 0000000..39c8b4c
--- /dev/null
+++ b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerListTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2019 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 static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.AudioManager;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.Looper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MediaPlayerListTest {
+    private MediaPlayerList mMediaPlayerList;
+
+    private @Captor ArgumentCaptor<AudioManager.AudioPlaybackCallback> mAudioCb;
+    private @Captor ArgumentCaptor<MediaPlayerWrapper.Callback> mPlayerWrapperCb;
+    private @Captor ArgumentCaptor<MediaData> mMediaUpdateData;
+    private @Mock Context mMockContext;
+    private @Mock AvrcpTargetService.ListCallback mAvrcpCallback;
+    private @Mock MediaController mMockController;
+    private @Mock MediaPlayerWrapper mMockPlayerWrapper;
+
+    private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false");
+    private MediaPlayerWrapper.Callback mActivePlayerCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        if (!mFlagDexmarker.equals("true")) {
+            System.setProperty("dexmaker.share_classloader", "true");
+        }
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        Assert.assertNotNull(Looper.myLooper());
+
+        MockitoAnnotations.initMocks(this);
+
+        AudioManager mockAudioManager = mock(AudioManager.class);
+        when(mMockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mockAudioManager);
+
+        MediaSessionManager mMediaSessionManager =
+                (MediaSessionManager) InstrumentationRegistry.getTargetContext()
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
+        PackageManager mockPackageManager = mock(PackageManager.class);
+        when(mMockContext.getSystemService(Context.MEDIA_SESSION_SERVICE))
+            .thenReturn(mMediaSessionManager);
+
+        mMediaPlayerList =
+            new MediaPlayerList(Looper.myLooper(), InstrumentationRegistry.getTargetContext());
+
+        when(mMockContext.registerReceiver(any(), any())).thenReturn(null);
+        when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
+        when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+        List<ResolveInfo> playerList = new ArrayList<ResolveInfo>();
+        when(mockPackageManager.queryIntentServices(any(), anyInt())).thenReturn(null);
+
+        Method method = BrowsablePlayerConnector.class.getDeclaredMethod("setInstanceForTesting",
+                BrowsablePlayerConnector.class);
+        BrowsablePlayerConnector mockConnector = mock(BrowsablePlayerConnector.class);
+        method.setAccessible(true);
+        method.invoke(null, mockConnector);
+        mMediaPlayerList.init(mAvrcpCallback);
+
+        MediaControllerFactory.inject(mMockController);
+        MediaPlayerWrapperFactory.inject(mMockPlayerWrapper);
+
+        doReturn("testPlayer").when(mMockController).getPackageName();
+        when(mMockPlayerWrapper.isMetadataSynced()).thenReturn(false);
+        mMediaPlayerList.setActivePlayer(mMediaPlayerList.addMediaPlayer(mMockController));
+
+        verify(mMockPlayerWrapper).registerCallback(mPlayerWrapperCb.capture());
+        mActivePlayerCallback = mPlayerWrapperCb.getValue();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Method method = BrowsablePlayerConnector.class.getDeclaredMethod("setInstanceForTesting",
+                BrowsablePlayerConnector.class);
+        method.setAccessible(true);
+        method.invoke(null, (BrowsablePlayerConnector) null);
+
+        MediaControllerFactory.inject(null);
+        MediaPlayerWrapperFactory.inject(null);
+        mMediaPlayerList.cleanup();
+        if (!mFlagDexmarker.equals("true")) {
+            System.setProperty("dexmaker.share_classloader", mFlagDexmarker);
+        }
+    }
+
+    private MediaData prepareMediaData(int playbackState) {
+        PlaybackState.Builder builder = new PlaybackState.Builder();
+        builder.setState(playbackState, 0, 1);
+        ArrayList<Metadata> list = new ArrayList<Metadata>();
+        list.add(Util.empty_data());
+        MediaData newData = new MediaData(
+                Util.empty_data(),
+                builder.build(),
+                list);
+
+        return newData;
+    }
+
+    @Test
+    public void testUpdateMeidaDataForAudioPlaybackWhenActivePlayNotPlaying() {
+        // Verify update media data with playing state
+        doReturn(prepareMediaData(PlaybackState.STATE_PAUSED))
+            .when(mMockPlayerWrapper).getCurrentMediaData();
+        mMediaPlayerList.injectAudioPlaybacActive(true);
+        verify(mAvrcpCallback).run(mMediaUpdateData.capture());
+        MediaData data = mMediaUpdateData.getValue();
+        Assert.assertEquals(data.state.getState(), PlaybackState.STATE_PLAYING);
+
+        // verify update media data with current media player media data
+        MediaData currentMediaData = prepareMediaData(PlaybackState.STATE_PAUSED);
+        doReturn(currentMediaData).when(mMockPlayerWrapper).getCurrentMediaData();
+        mMediaPlayerList.injectAudioPlaybacActive(false);
+        verify(mAvrcpCallback, times(2)).run(mMediaUpdateData.capture());
+        data = mMediaUpdateData.getValue();
+        Assert.assertEquals(data.metadata, currentMediaData.metadata);
+        Assert.assertEquals(data.state.toString(), currentMediaData.state.toString());
+        Assert.assertEquals(data.queue, currentMediaData.queue);
+    }
+
+    @Test
+    public void testUpdateMediaDataForActivePlayerWhenAudioPlaybackIsNotActive() {
+        MediaData currMediaData = prepareMediaData(PlaybackState.STATE_PLAYING);
+        mActivePlayerCallback.mediaUpdatedCallback(currMediaData);
+        verify(mAvrcpCallback).run(currMediaData);
+
+        currMediaData = prepareMediaData(PlaybackState.STATE_PAUSED);
+        mActivePlayerCallback.mediaUpdatedCallback(currMediaData);
+        verify(mAvrcpCallback).run(currMediaData);
+    }
+
+    @Test
+    public void testNotUpdateMediaDataForAudioPlaybackWhenActivePlayerIsPlaying() {
+        // Verify not update media data for Audio Playback when active player is playing
+        doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
+            .when(mMockPlayerWrapper).getCurrentMediaData();
+        mMediaPlayerList.injectAudioPlaybacActive(true);
+        mMediaPlayerList.injectAudioPlaybacActive(false);
+        verify(mAvrcpCallback, never()).run(any());
+    }
+
+    @Test
+    public void testNotUpdateMediaDataForActivePlayerWhenAudioPlaybackIsActive() {
+        doReturn(prepareMediaData(PlaybackState.STATE_PLAYING))
+            .when(mMockPlayerWrapper).getCurrentMediaData();
+        mMediaPlayerList.injectAudioPlaybacActive(true);
+        verify(mAvrcpCallback, never()).run(any());
+
+        // Verify not update active player media data when audio playback is active
+        mActivePlayerCallback.mediaUpdatedCallback(prepareMediaData(PlaybackState.STATE_PAUSED));
+        verify(mAvrcpCallback, never()).run(any());
+    }
+
+}
diff --git a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
index ee41320..1e41033 100644
--- a/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
+++ b/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java
@@ -138,10 +138,10 @@
      */
     @Test
     public void testNullControllerLooper() {
-        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper());
+        MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(null, mThread.getLooper());
         Assert.assertNull(wrapper);
 
-        wrapper = MediaPlayerWrapper.wrap(mMockController, null);
+        wrapper = MediaPlayerWrapperFactory.wrap(mMockController, null);
         Assert.assertNull(wrapper);
     }
 
@@ -151,7 +151,8 @@
      */
     @Test
     public void testIsReady() {
-        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         Assert.assertTrue(wrapper.isPlaybackStateReady());
         Assert.assertTrue(wrapper.isMetadataReady());
 
@@ -179,7 +180,8 @@
     @Test
     public void testControllerUpdate() {
         // Create the wrapper object and register the looper with the timeout handler
-        MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         Assert.assertTrue(wrapper.isPlaybackStateReady());
         Assert.assertTrue(wrapper.isMetadataReady());
         wrapper.registerCallback(mTestCbs);
@@ -213,7 +215,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Return null when getting the queue
@@ -275,7 +277,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Return null when getting the queue
@@ -319,7 +321,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Return null when getting the queue
@@ -348,7 +350,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Return null when getting the queue
@@ -375,7 +377,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Call getCurrentQueue() multiple times.
@@ -398,7 +400,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Return null when getting the queue
@@ -455,7 +457,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Cleanup the wrapper
@@ -475,7 +477,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Grab the callbacks the wrapper registered with the controller
@@ -505,7 +507,7 @@
         // 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());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Grab the callbacks the wrapper registered with the controller
@@ -565,7 +567,7 @@
                 InstrumentationRegistry.getInstrumentation()
                         .acquireLooperManager(mThread.getLooper());
         MediaPlayerWrapper wrapper =
-                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Grab the callbacks the wrapper registered with the controller
@@ -615,7 +617,7 @@
                 InstrumentationRegistry.getInstrumentation()
                         .acquireLooperManager(mThread.getLooper());
         MediaPlayerWrapper wrapper =
-                MediaPlayerWrapper.wrap(mMockController, mThread.getLooper());
+                MediaPlayerWrapperFactory.wrap(mMockController, mThread.getLooper());
         wrapper.registerCallback(mTestCbs);
 
         // Grab the callbacks the wrapper registered with the controller