Show the output switcher when no media is playing

- Support output switcher for system routing.
- Add an new string to indicate the device that
  audio will output to.

Bug: 284227163
Test: device/host atest
 atest MediaOutputPreferenceControllerTest
 atest AudioOutputSwitchPreferenceControllerTest
 atest MediaOutputIndicatorSliceTest

Change-Id: I94bcf84e7e93b3e4f5db1d95d5380a54a3e0c460
Signed-off-by: Jasmine Cha <chajasmine@google.com>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7998c1e..6ed0d82 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10927,6 +10927,9 @@
     <!-- Title with application label for media output settings. [CHAR LIMIT=NONE] -->
     <string name="media_output_label_title">Play <xliff:g id="label" example="Music Player">%s</xliff:g> on</string>
 
+    <!-- Title for media output settings without media is playing -->
+    <string name="media_output_title_without_playing">Audio will play on</string>
+
     <!-- Summary for media output default settings. (this device) [CHAR LIMIT=30] -->
     <string name="media_output_default_summary">This device</string>
 
diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
index e2232e7..bb075c2 100644
--- a/src/com/android/settings/media/MediaOutputIndicatorSlice.java
+++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
@@ -17,6 +17,7 @@
 package com.android.settings.media;
 
 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
+import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;
 
 import android.annotation.ColorInt;
 import android.content.Context;
@@ -58,7 +59,12 @@
         }
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 com.android.internal.R.drawable.ic_settings_bluetooth);
-        final CharSequence title = mContext.getString(R.string.media_output_label_title,
+        final int stringRes = enableOutputSwitcherForSystemRouting()
+                ? (getWorker().getActiveLocalMediaController() != null
+                        ? R.string.media_output_label_title
+                        : R.string.media_output_title_without_playing)
+                : R.string.media_output_label_title;
+        final CharSequence title = mContext.getString(stringRes,
                 Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
         final SliceAction primarySliceAction = SliceAction.create(
                 getBroadcastIntent(mContext), icon, ListBuilder.ICON_IMAGE, title);
@@ -117,28 +123,36 @@
         // 2. worker is not null
         // 3. Available devices are more than 0
         // 4. The local media session is active and the state is playing.
+        //    - if !enableOutputSwitcherForSystemRouting(), (4) will be bypass.
         return getWorker() != null
                 && !com.android.settingslib.Utils.isAudioModeOngoingCall(mContext)
                 && getWorker().getMediaDevices().size() > 0
-                && getWorker().getActiveLocalMediaController() != null;
+                && (enableOutputSwitcherForSystemRouting()
+                        ? true : getWorker().getActiveLocalMediaController() != null);
     }
 
     @Override
     public void onNotifyChange(Intent intent) {
         final MediaController mediaController = getWorker().getActiveLocalMediaController();
 
-        if (mediaController == null) {
+        // Launch media output dialog
+        if (enableOutputSwitcherForSystemRouting() && mediaController == null) {
+            mContext.sendBroadcast(new Intent()
+                    .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
+                    .setAction(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG));
+        } else if (mediaController != null) {
+            mContext.sendBroadcast(new Intent()
+                    .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
+                    .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
+                    .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
+                            mediaController.getSessionToken())
+                    .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
+                            mediaController.getPackageName()));
+        } else {
             Log.d(TAG, "No active local media controller");
             return;
         }
-        // Launch media output dialog
-        mContext.sendBroadcast(new Intent()
-                .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
-                .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
-                .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
-                        mediaController.getSessionToken())
-                .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
-                        mediaController.getPackageName()));
+
         // Dismiss volume panel
         mContext.sendBroadcast(new Intent()
                 .setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME)
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 6475257..b570b2d 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -18,6 +18,9 @@
 
 import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
 
+import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;
+
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -28,6 +31,8 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.MediaRouter;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.FeatureFlagUtils;
@@ -79,6 +84,8 @@
     private final WiredHeadsetBroadcastReceiver mReceiver;
     private final Handler mHandler;
     private LocalBluetoothManager mLocalBluetoothManager;
+    @Nullable private MediaSessionManager.OnActiveSessionsChangedListener mSessionListener;
+    @Nullable private MediaSessionManager mMediaSessionManager;
 
     public interface AudioSwitchCallback {
         void onPreferenceDataChanged(ListPreference preference);
@@ -107,6 +114,14 @@
             return;
         }
         mProfileManager = mLocalBluetoothManager.getProfileManager();
+
+        if (enableOutputSwitcherForSystemRouting()) {
+            mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+            mSessionListener = new SessionChangeListener();
+        } else {
+            mMediaSessionManager = null;
+            mSessionListener = null;
+        }
     }
 
     /**
@@ -329,13 +344,27 @@
         // Register for misc other intent broadcasts.
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
         intentFilter.addAction(STREAM_DEVICES_CHANGED_ACTION);
-        mContext.registerReceiver(mReceiver, intentFilter);
+
+        if (enableOutputSwitcherForSystemRouting()) {
+            mContext.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED);
+            if (mMediaSessionManager != null) {
+                mMediaSessionManager.addOnActiveSessionsChangedListener(
+                        mSessionListener, null, mHandler);
+            }
+        } else {
+            mContext.registerReceiver(mReceiver, intentFilter);
+        }
     }
 
     private void unregister() {
         mLocalBluetoothManager.getEventManager().unregisterCallback(this);
         mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback);
         mContext.unregisterReceiver(mReceiver);
+        if (enableOutputSwitcherForSystemRouting()) {
+            if (mMediaSessionManager != null) {
+                mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionListener);
+            }
+        }
     }
 
     /** Notifications of audio device connection and disconnection events. */
@@ -362,4 +391,12 @@
             }
         }
     }
+
+    private class SessionChangeListener
+            implements MediaSessionManager.OnActiveSessionsChangedListener {
+        @Override
+        public void onActiveSessionsChanged(List<MediaController> controllers) {
+            updateState(mPreference);
+        }
+    }
 }
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index 758f7e3..47be7fe 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.sound;
 
+import static com.android.settingslib.media.flags.Flags.enableOutputSwitcherForSystemRouting;
+
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.Intent;
@@ -46,21 +49,22 @@
  */
 public class MediaOutputPreferenceController extends AudioSwitchPreferenceController {
 
-    private MediaController mMediaController;
+    private static final String TAG = "MediaOutputPreferenceController";
+    @Nullable private MediaController mMediaController;
+    private MediaSessionManager mMediaSessionManager;
 
     public MediaOutputPreferenceController(Context context, String key) {
         super(context, key);
-        mMediaController = MediaOutputUtils.getActiveLocalMediaController(context.getSystemService(
-                MediaSessionManager.class));
+        mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+        mMediaController = MediaOutputUtils.getActiveLocalMediaController(mMediaSessionManager);
     }
 
     @Override
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
 
-        if (!Utils.isAudioModeOngoingCall(mContext) && mMediaController != null) {
-            mPreference.setVisible(true);
-        }
+        mPreference.setVisible(!Utils.isAudioModeOngoingCall(mContext)
+                && (enableOutputSwitcherForSystemRouting() ? true : mMediaController != null));
     }
 
     @Override
@@ -70,11 +74,16 @@
             return;
         }
 
-        if (mMediaController == null) {
-            // No active local playback
-            return;
+        if (enableOutputSwitcherForSystemRouting()) {
+            mMediaController = MediaOutputUtils.getActiveLocalMediaController(mMediaSessionManager);
+        } else {
+            if (mMediaController == null) {
+                // No active local playback
+                return;
+            }
         }
 
+
         if (Utils.isAudioModeOngoingCall(mContext)) {
             // Ongoing call status, switch entry for media will be disabled.
             mPreference.setVisible(false);
@@ -95,9 +104,14 @@
                 || (connectedLeAudioDevices != null && !connectedLeAudioDevices.isEmpty()))) {
             activeDevice = findActiveDevice();
         }
-        mPreference.setTitle(mContext.getString(R.string.media_output_label_title,
-                com.android.settings.Utils.getApplicationLabel(mContext,
-                        mMediaController.getPackageName())));
+
+        if (mMediaController == null) {
+            mPreference.setTitle(mContext.getString(R.string.media_output_title_without_playing));
+        } else {
+            mPreference.setTitle(mContext.getString(R.string.media_output_label_title,
+                    com.android.settings.Utils.getApplicationLabel(mContext,
+                    mMediaController.getPackageName())));
+        }
         mPreference.setSummary((activeDevice == null) ?
                 mContext.getText(R.string.media_output_default_summary) :
                 activeDevice.getAlias());
@@ -145,13 +159,19 @@
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
         if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
-            mContext.sendBroadcast(new Intent()
-                    .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
-                    .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
-                    .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
-                            mMediaController.getPackageName())
-                    .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
-                            mMediaController.getSessionToken()));
+            if (enableOutputSwitcherForSystemRouting() && mMediaController == null) {
+                mContext.sendBroadcast(new Intent()
+                        .setAction(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG)
+                        .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME));
+            } else if (mMediaController != null) {
+                mContext.sendBroadcast(new Intent()
+                        .setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
+                        .setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
+                        .putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
+                                mMediaController.getPackageName())
+                        .putExtra(MediaOutputConstants.KEY_MEDIA_SESSION_TOKEN,
+                                mMediaController.getSessionToken()));
+            }
             return true;
         }
         return false;
diff --git a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java
index a91f627..652bd03 100644
--- a/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaOutputIndicatorSliceTest.java
@@ -18,6 +18,7 @@
 package com.android.settings.media;
 
 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_INDICATOR_SLICE_URI;
+import static com.android.settingslib.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -38,6 +39,7 @@
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Process;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.text.TextUtils;
 
 import androidx.slice.Slice;
@@ -55,6 +57,7 @@
 
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -96,6 +99,9 @@
     @Mock
     private Drawable mTestDrawable;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private Context mContext;
     private MediaOutputIndicatorSlice mMediaOutputIndicatorSlice;
     private AudioManager mAudioManager;
@@ -255,6 +261,34 @@
     }
 
     @Test
+    public void onNotifyChange_withoutMediaControllerFlagEnabled_verifyIntentExtra() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        doReturn(null).when(sMediaOutputIndicatorWorker)
+                .getActiveLocalMediaController();
+        ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
+
+        mMediaOutputIndicatorSlice.onNotifyChange(null);
+        verify(mContext, times(2)).sendBroadcast(argument.capture());
+        List<Intent> intentList = argument.getAllValues();
+        Intent intent = intentList.get(0);
+
+        assertThat(intent.getAction()).isEqualTo(
+                MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
+        assertThat(TextUtils.equals(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME,
+                intent.getPackage())).isTrue();
+    }
+
+    @Test
+    public void onNotifyChange_withoutMediaControllerFlagDisabled_doNothing() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        doReturn(null).when(sMediaOutputIndicatorWorker)
+                .getActiveLocalMediaController();
+
+        mMediaOutputIndicatorSlice.onNotifyChange(null);
+    }
+
+
+    @Test
     public void isVisible_allConditionMatched_returnTrue() {
         mAudioManager.setMode(AudioManager.MODE_NORMAL);
         mDevices.add(mDevice1);
@@ -268,6 +302,7 @@
 
     @Test
     public void isVisible_noActiveSession_returnFalse() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
         mAudioManager.setMode(AudioManager.MODE_NORMAL);
         mDevices.add(mDevice1);
 
@@ -278,6 +313,19 @@
         assertThat(mMediaOutputIndicatorSlice.isVisible()).isFalse();
     }
 
+    @Test
+    public void isVisible_noActiveSession_returnTrue() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        mAudioManager.setMode(AudioManager.MODE_NORMAL);
+        mDevices.add(mDevice1);
+
+        when(sMediaOutputIndicatorWorker.getMediaDevices()).thenReturn(mDevices);
+        doReturn(mMediaController).when(sMediaOutputIndicatorWorker)
+                .getActiveLocalMediaController();
+
+        assertThat(mMediaOutputIndicatorSlice.isVisible()).isTrue();
+    }
+
     private void initPackage() {
         mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
         mAppInfo = new ApplicationInfo();
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index 151d1f2..a272d9c 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -21,10 +21,12 @@
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
 import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settingslib.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -38,6 +40,8 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.media.AudioManager;
+import android.media.session.MediaSessionManager;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.FeatureFlagUtils;
 
 import androidx.preference.ListPreference;
@@ -61,6 +65,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -113,6 +118,9 @@
     @Mock
     private CachedBluetoothDevice mCachedBluetoothDeviceR;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private Context mContext;
     private PreferenceScreen mScreen;
     private ListPreference mPreference;
@@ -238,6 +246,7 @@
 
     @Test
     public void onStart_shouldRegisterCallbackAndRegisterReceiver() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
         mController.onStart();
 
         verify(mLocalBluetoothManager.getEventManager()).registerCallback(
@@ -248,6 +257,7 @@
 
     @Test
     public void onStop_shouldUnregisterCallbackAndUnregisterReceiver() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
         mController.onStart();
         mController.onStop();
 
@@ -257,6 +267,45 @@
         verify(mLocalBluetoothManager).setForegroundActivity(null);
     }
 
+    @Test
+    public void onStart_shouldRegisterCallbackAndRegisterReceiverWithDefaultMediaOutput() {
+        MediaSessionManager mediaSessionManager =
+                spy(mContext.getSystemService(MediaSessionManager.class));
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mediaSessionManager);
+        mController = new AudioSwitchPreferenceControllerTestable(mContext, TEST_KEY);
+
+        mController.onStart();
+
+        verify(mLocalBluetoothManager.getEventManager()).registerCallback(
+                any(BluetoothCallback.class));
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
+                eq(Context.RECEIVER_NOT_EXPORTED));
+        verify(mLocalBluetoothManager).setForegroundActivity(mContext);
+        verify(mediaSessionManager).addOnActiveSessionsChangedListener(
+                any(MediaSessionManager.OnActiveSessionsChangedListener.class), any(), any());
+    }
+
+
+    @Test
+    public void onStop_shouldUnregisterCallbackAndUnregisterReceiverWithDefaultMediaOutput() {
+        MediaSessionManager mediaSessionManager =
+                spy(mContext.getSystemService(MediaSessionManager.class));
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mediaSessionManager);
+        mController = new AudioSwitchPreferenceControllerTestable(mContext, TEST_KEY);
+        mController.onStart();
+
+        mController.onStop();
+
+        verify(mLocalBluetoothManager.getEventManager()).unregisterCallback(
+                any(BluetoothCallback.class));
+        verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+        verify(mLocalBluetoothManager).setForegroundActivity(null);
+        verify(mediaSessionManager).removeOnActiveSessionsChangedListener(
+                any(MediaSessionManager.OnActiveSessionsChangedListener.class));
+    }
+
     /**
      * Audio stream output to bluetooth sco headset which is the subset of all sco device.
      * isStreamFromOutputDevice should return true.
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index 5a92a08..b9f9b16 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -21,6 +21,8 @@
 import static android.media.AudioSystem.DEVICE_OUT_EARPIECE;
 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
 
+import static com.android.settingslib.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -45,6 +47,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
@@ -66,6 +69,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -103,6 +107,9 @@
     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
     private static final String TEST_APPLICATION_LABEL = "APP Test Label";
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Mock
     private LocalBluetoothManager mLocalManager;
     @Mock
@@ -227,6 +234,8 @@
         mScreen.addPreference(mPreference);
         mController.displayPreference(mScreen);
         mController.setCallback(mAudioSwitchPreferenceCallback);
+
+        mSetFlagsRule.initAllFlagsToReleaseConfigDefault();
     }
 
     @After
@@ -314,6 +323,7 @@
 
     @Test
     public void updateState_noActiveLocalPlayback_noTitle() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
         mPlaybackState = new PlaybackState.Builder()
                 .setState(PlaybackState.STATE_NONE, 0, 1)
                 .build();
@@ -326,6 +336,48 @@
     }
 
     @Test
+    public void updateState_noActiveLocalPlayback_checkTitle() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        mPlaybackState = new PlaybackState.Builder()
+                .setState(PlaybackState.STATE_NONE, 0, 1)
+                .build();
+        when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+        mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.media_output_title_without_playing,
+                        TEST_APPLICATION_LABEL));
+    }
+
+    @Test
+    public void updateState_withNullMediaController_noTitle() {
+        mSetFlagsRule.disableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        mMediaControllers.clear();
+        mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.getTitle()).isNull();
+    }
+
+    @Test
+    public void updateState_withNullMediaController_checkTitle() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        mMediaControllers.clear();
+        mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.getTitle().toString()).isEqualTo(
+                mContext.getString(R.string.media_output_title_without_playing,
+                        TEST_APPLICATION_LABEL));
+    }
+
+    @Test
     public void updateState_withActiveLocalPlayback_checkTitle() {
         initPackage();
         mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
@@ -349,6 +401,39 @@
                 .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG);
     }
 
+    @Test
+    public void handlePreferenceTreeClick_WithNoLocalPlaybackFlagEnabled_verifyIntentExtra() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        mPlaybackState = new PlaybackState.Builder()
+                .setState(PlaybackState.STATE_NONE, 0, 1)
+                .build();
+        when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+        mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
+        mPreference.setKey(TEST_KEY);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mContext).sendBroadcast(intentCaptor.capture());
+        assertThat(intentCaptor.getValue().getAction())
+                .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_WithNullControllerFlagEnabled_verifyIntentExtra() {
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_OUTPUT_SWITCHER_FOR_SYSTEM_ROUTING);
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        mMediaControllers.clear();
+        mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
+        mPreference.setKey(TEST_KEY);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        verify(mContext).sendBroadcast(intentCaptor.capture());
+        assertThat(intentCaptor.getValue().getAction())
+                .isEqualTo(MediaOutputConstants.ACTION_LAUNCH_SYSTEM_MEDIA_OUTPUT_DIALOG);
+    }
+
     /**
      * Default status
      * Preference should be invisible