Merge "[Settings] Query historical data in background" into rvc-dev
diff --git a/res/layout/network_request_dialog_title.xml b/res/layout/network_request_dialog_title.xml
index b61a7db..4385a88 100644
--- a/res/layout/network_request_dialog_title.xml
+++ b/res/layout/network_request_dialog_title.xml
@@ -18,28 +18,43 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingEnd="?android:attr/dialogPreferredPadding"
-    android:orientation="horizontal"
+    android:orientation="vertical"
     android:background="?android:attr/selectableItemBackground"
     android:minHeight="?android:attr/listPreferredItemHeightSmall">
 
-  <TextView
-      android:id="@+id/network_request_title_text"
-      android:layout_width="0dp"
-      android:layout_height="wrap_content"
-      android:paddingLeft="24dp"
-      android:paddingTop="18dp"
-      android:layout_weight="1"
-      android:textSize="18sp"
-      android:gravity="center_vertical"
-      style="@style/info_label"/>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingStart="24dp">
 
-  <ProgressBar
-      android:id="@+id/network_request_title_progress"
-      style="?android:attr/progressBarStyleSmallTitle"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_gravity="center_vertical"
-      android:layout_marginStart="16dip"
-      android:minWidth="32dp"
-      android:text="@string/progress_scanning"/>
+        <TextView
+            android:id="@+id/network_request_title_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:paddingTop="18dp"
+            android:layout_weight="1"
+            android:textSize="20sp"
+            android:gravity="center_vertical"
+            style="@style/info_label"/>
+
+        <ProgressBar
+            android:id="@+id/network_request_title_progress"
+            style="?android:attr/progressBarStyleSmallTitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="20dp"
+            android:gravity="center_vertical"
+            android:layout_marginStart="16dip"
+            android:minWidth="32dp"
+            android:text="@string/progress_scanning"/>
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/network_request_summary_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingTop="18dp"
+        android:textSize="16sp"/>
 </LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 18673e4..1dc84b6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -176,6 +176,8 @@
     <string name="bluetooth_empty_list_user_restricted">You don\u2019t have permission to change Bluetooth settings.</string>
     <!-- Title for bluetooth pairing item [CHAR LIMIT=60] -->
     <string name="bluetooth_pairing_pref_title">Pair new device</string>
+    <!-- Keywords for bluetooth pairing item [CHAR LIMIT=30] -->
+    <string name="keywords_add_bt_device">bluetooth</string>
 
     <!-- Bluetooth Visibility message.  This message informs the user that their device is now visible to other bluetooth devices.  [CHAR LIMIT=NONE] -->
     <string name="bluetooth_is_visible_message"><xliff:g id="device_name">%1$s</xliff:g> is visible to nearby devices while Bluetooth settings is open.</string>
@@ -8470,7 +8472,7 @@
     <string name="notification_priority_title">Priority</string>
 
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
-    <string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble.</string>
+    <string name="notification_channel_summary_priority">Shows at top of conversation section and appears as a bubble</string>
 
     <string name="convo_not_supported_summary"><xliff:g id="app_name" example="Android Services">%1$s</xliff:g> does not support conversation-specific settings.</string>
 
@@ -8491,6 +8493,9 @@
     <!-- [CHAR LIMIT=100] Label for on/off toggle -->
     <string name="notification_switch_label">All \"<xliff:g id="app_name" example="Android Services">%1$s</xliff:g>\" notifications</string>
 
+    <!-- [CHAR LIMIT=100] Label for on/off toggle -->
+    <string name="notification_app_switch_label">All <xliff:g id="app_name" example="Android Services">%1$s</xliff:g> notifications</string>
+
     <!-- Default Apps > Default notification assistant -->
     <string name="default_notification_assistant">Adaptive Notifications</string>
 
@@ -11736,7 +11741,12 @@
     <string name="see_less">See less</string>
 
     <!-- Title for Network connection request Dialog [CHAR LIMIT=60] -->
-    <string name="network_connection_request_dialog_title">Device to use with <xliff:g id="appName" example="ThirdPartyAppName">%1$s</xliff:g></string>
+    <string name="network_connection_request_dialog_title">Connect to device</string>
+    <!-- Summary for Network connection request Dialog [CHAR LIMIT=NONE] -->
+    <string name="network_connection_request_dialog_summary">
+        <xliff:g id="appName" example="ThirdPartyAppName">%1$s</xliff:g>
+        app wants to use a temporary Wi\u2011Fi network to connect to your device
+    </string>
     <!-- Message for Network connection timeout Dialog [CHAR LIMIT=NONE] -->
     <string name="network_connection_timeout_dialog_message">No devices found. Make sure devices are turned on and available to connect.</string>
     <!-- OK button for Network connection timeout Dialog [CHAR LIMIT=30] -->
@@ -11795,6 +11805,11 @@
     <!-- Summary for the top level Privacy Settings [CHAR LIMIT=NONE]-->
     <string name="privacy_dashboard_summary">Permissions, account activity, personal data</string>
 
+    <!-- UI debug setting: show media player on quick settings title [CHAR LIMIT=60] -->
+    <string name="quick_settings_media_player">Media resumption</string>
+    <!-- UI debug setting: show media player on quick settings summary [CHAR_LIMIT=NONE] -->
+    <string name="quick_settings_media_player_summary">Shows and persists media player in Quick Settings. Requires reboot.</string>
+
     <!-- Label for button in contextual card for users to remove the card [CHAR LIMIT=30] -->
     <string name="contextual_card_dismiss_remove">Remove</string>
     <!-- Label for button in contextual card for users to keep the card [CHAR LIMIT=30] -->
diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml
index a1736b2..b2794c2 100644
--- a/res/xml/connected_devices.xml
+++ b/res/xml/connected_devices.xml
@@ -43,6 +43,7 @@
         android:summary="@string/connected_device_add_device_summary"
         android:fragment="com.android.settings.bluetooth.BluetoothPairingDetail"
         settings:allowDividerAbove="true"
+        settings:keywords="@string/keywords_add_bt_device"
         settings:userRestriction="no_config_bluetooth"
         settings:useAdminDisabledSummary="true"
         settings:controller="com.android.settings.connecteddevice.AddDevicePreferenceController"/>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 6dcc125..39a130c 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -525,6 +525,11 @@
             android:title="@string/usb_audio_disable_routing"
             android:summary="@string/usb_audio_disable_routing_summary" />
 
+        <SwitchPreference
+            android:key="quick_settings_media_player"
+            android:title="@string/quick_settings_media_player"
+            android:summary="@string/quick_settings_media_player_summary" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index a063327..a86b441 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -63,6 +63,7 @@
         LifecycleObserver, OnStart, OnStop, OnDestroy, CachedBluetoothDevice.Callback {
     private static final String TAG = "AdvancedBtHeaderCtrl";
     private static final int LOW_BATTERY_LEVEL = 15;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     @VisibleForTesting
     LayoutPreference mLayoutPreference;
@@ -215,6 +216,11 @@
 
         final int batteryLevel = BluetoothUtils.getIntMetaData(bluetoothDevice, batteryMetaKey);
         final boolean charging = BluetoothUtils.getBooleanMetaData(bluetoothDevice, chargeMetaKey);
+        if (DBG) {
+            Log.d(TAG, "updateSubLayout() icon : " + iconMetaKey + ", battery : " + batteryMetaKey
+                    + ", charge : " + chargeMetaKey + ", batteryLevel : " + batteryLevel
+                    + ", charging : " + charging + ", iconUri : " + iconUri);
+        }
         if (batteryLevel != BluetoothUtils.META_INT_ERROR) {
             linearLayout.setVisibility(View.VISIBLE);
             final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
@@ -268,6 +274,9 @@
         final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
         final String iconUri = BluetoothUtils.getStringMetaData(bluetoothDevice,
                 BluetoothDevice.METADATA_MAIN_ICON);
+        if (DBG) {
+            Log.d(TAG, "updateDisconnectLayout() iconUri : " + iconUri);
+        }
         if (iconUri != null) {
             final ImageView imageView = linearLayout.findViewById(R.id.header_icon);
             updateIcon(imageView, iconUri);
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index aa4f869..27d63bf 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -33,7 +33,7 @@
         implements Preference.OnPreferenceClickListener {
 
     private static final String TAG = "AvailableMediaBluetoothDeviceUpdater";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String PREF_KEY = "available_media_bt";
 
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 21312a5..8cb698f 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -52,7 +52,7 @@
 public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
         LocalBluetoothProfileManager.ServiceListener {
     private static final String TAG = "BluetoothDeviceUpdater";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     protected final MetricsFeatureProvider mMetricsFeatureProvider;
     protected final DevicePreferenceCallback mDevicePreferenceCallback;
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index c7cae7f..fc1b9b7 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -33,7 +33,7 @@
 public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
 
     private static final String TAG = "ConnBluetoothDeviceUpdater";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String PREF_KEY = "connected_bt";
 
diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
index bbcd13c..466d60e 100644
--- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
@@ -38,7 +38,7 @@
         implements Preference.OnPreferenceClickListener {
 
     private static final String TAG = "SavedBluetoothDeviceUpdater";
-    private static final boolean DBG = false;
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String PREF_KEY = "saved_bt";
 
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 878d442..0594ef5 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -491,6 +491,7 @@
         controllers.add(new DebugNonRectClipOperationsPreferenceController(context));
         controllers.add(new ForceDarkPreferenceController(context));
         controllers.add(new EnableBlursPreferenceController(context));
+        controllers.add(new QuickSettingsMediaPlayerPreferenceController(context));
         controllers.add(new ForceMSAAPreferenceController(context));
         controllers.add(new HardwareOverlaysPreferenceController(context));
         controllers.add(new SimulateColorSpacePreferenceController(context));
diff --git a/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceController.java b/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceController.java
new file mode 100644
index 0000000..47f3adc
--- /dev/null
+++ b/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 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.settings.development;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Controls whether the media player should be visible in quick settings.
+ */
+public class QuickSettingsMediaPlayerPreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+        PreferenceControllerMixin {
+    private static final String PREFERENCE_KEY = "quick_settings_media_player";
+    @VisibleForTesting
+    static final String SETTING_NAME = Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS;
+    @VisibleForTesting
+    static final int SETTING_VALUE_ON = 1;
+    @VisibleForTesting
+    static final int SETTING_VALUE_OFF = 0;
+
+    public QuickSettingsMediaPlayerPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREFERENCE_KEY;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean isEnabled = (Boolean) newValue;
+        Settings.Global.putInt(mContext.getContentResolver(), SETTING_NAME,
+                isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(), SETTING_NAME,
+                SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
+    }
+
+    @Override
+    protected void onDeveloperOptionsSwitchDisabled() {
+        super.onDeveloperOptionsSwitchDisabled();
+        Settings.Global.putInt(mContext.getContentResolver(), SETTING_NAME, SETTING_VALUE_OFF);
+        ((SwitchPreference) mPreference).setChecked(false);
+    }
+}
diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
index c768257..52249ff 100644
--- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java
+++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
+import android.media.RoutingSessionInfo;
 import android.net.Uri;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -173,6 +174,10 @@
         return mLocalMediaManager.getSelectedMediaDevice();
     }
 
+    void adjustSessionVolume(String sessionId, int volume) {
+        mLocalMediaManager.adjustSessionVolume(sessionId, volume);
+    }
+
     void adjustSessionVolume(int volume) {
         mLocalMediaManager.adjustSessionVolume(volume);
     }
@@ -189,15 +194,14 @@
         return mLocalMediaManager.getSessionName();
     }
 
-    /**
-     * Find the active MediaDevice.
-     *
-     * @param type the media device type.
-     * @return MediaDevice list
-     *
-     */
-    public List<MediaDevice> getActiveMediaDevice(@MediaDevice.MediaDeviceType int type) {
-        return mLocalMediaManager.getActiveMediaDevice(type);
+    List<RoutingSessionInfo> getActiveRemoteMediaDevice() {
+        final List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
+        for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
+            if (!info.isSystemSession()) {
+                sessionInfos.add(info);
+            }
+        }
+        return sessionInfos;
     }
 
     /**
diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java
index 4e442b7..71a41b3 100644
--- a/src/com/android/settings/media/RemoteMediaSlice.java
+++ b/src/com/android/settings/media/RemoteMediaSlice.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.media.RoutingSessionInfo;
 import android.net.Uri;
 import android.text.TextUtils;
 import android.util.Log;
@@ -41,7 +42,6 @@
 import com.android.settings.slices.SliceBackgroundWorker;
 import com.android.settings.slices.SliceBroadcastReceiver;
 import com.android.settings.slices.SliceBuilderUtils;
-import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.media.MediaOutputSliceConstants;
 
 import java.util.List;
@@ -67,7 +67,7 @@
         final int newPosition = intent.getIntExtra(EXTRA_RANGE_VALUE, -1);
         final String id = intent.getStringExtra(MEDIA_ID);
         if (!TextUtils.isEmpty(id)) {
-            getWorker().adjustVolume(getWorker().getMediaDeviceById(id), newPosition);
+            getWorker().adjustSessionVolume(id, newPosition);
         }
     }
 
@@ -80,9 +80,8 @@
             return listBuilder.build();
         }
         // Only displaying remote devices
-        final List<MediaDevice> mediaDevices = getWorker().getActiveMediaDevice(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
-        if (mediaDevices.isEmpty()) {
+        final List<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevice();
+        if (infos.isEmpty()) {
             Log.d(TAG, "No active remote media device");
             return listBuilder.build();
         }
@@ -93,27 +92,25 @@
         // To create an empty icon to indent the row
         final IconCompat emptyIcon = createEmptyIcon();
         int requestCode = 0;
-        for (MediaDevice mediaDevice : mediaDevices) {
-            final int maxVolume = mediaDevice.getMaxVolume();
+        for (RoutingSessionInfo info : infos) {
+            final int maxVolume = info.getVolumeMax();
             if (maxVolume <= 0) {
-                Log.d(TAG, "Unable to add Slice. " + mediaDevice.getName() + ": max volume is "
+                Log.d(TAG, "Unable to add Slice. " + info.getName() + ": max volume is "
                         + maxVolume);
                 continue;
             }
-            final String title = castVolume + " (" + mediaDevice.getClientAppLabel() + ")";
             listBuilder.addInputRange(new InputRangeBuilder()
                     .setTitleItem(icon, ListBuilder.ICON_IMAGE)
-                    .setTitle(title)
-                    .setInputAction(getSliderInputAction(requestCode++, mediaDevice.getId()))
-                    .setPrimaryAction(getSoundSettingAction(title, icon, mediaDevice.getId()))
+                    .setTitle(castVolume)
+                    .setInputAction(getSliderInputAction(requestCode++, info.getId()))
+                    .setPrimaryAction(getSoundSettingAction(castVolume, icon, info.getId()))
                     .setMax(maxVolume)
-                    .setValue(mediaDevice.getCurrentVolume()));
+                    .setValue(info.getVolume()));
             listBuilder.addRow(new ListBuilder.RowBuilder()
                     .setTitle(outputTitle)
-                    .setSubtitle(mediaDevice.getName())
+                    .setSubtitle(info.getName())
                     .setTitleItem(emptyIcon, ListBuilder.ICON_IMAGE)
-                    .setPrimaryAction(getMediaOutputSliceAction(
-                            mediaDevice.getClientPackageName())));
+                    .setPrimaryAction(getMediaOutputSliceAction(info.getClientPackageName())));
         }
         return listBuilder.build();
     }
@@ -131,7 +128,8 @@
         return PendingIntent.getBroadcast(mContext, requestCode, intent, 0);
     }
 
-    private SliceAction getSoundSettingAction(String actionTitle, IconCompat icon, String id) {
+    private SliceAction getSoundSettingAction(CharSequence actionTitle, IconCompat icon,
+            String id) {
         final Uri contentUri = new Uri.Builder().appendPath(id).build();
         final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
                 SoundSettings.class.getName(),
diff --git a/src/com/android/settings/network/ActiveSubsciptionsListener.java b/src/com/android/settings/network/ActiveSubsciptionsListener.java
index 99dfd55..3d15025 100644
--- a/src/com/android/settings/network/ActiveSubsciptionsListener.java
+++ b/src/com/android/settings/network/ActiveSubsciptionsListener.java
@@ -73,6 +73,7 @@
      * @param context {@code Context} of this listener
      */
     public ActiveSubsciptionsListener(Looper looper, Context context) {
+        super(looper);
         mLooper = looper;
         mContext = context;
 
diff --git a/src/com/android/settings/network/ProxySubscriptionManager.java b/src/com/android/settings/network/ProxySubscriptionManager.java
index 0306b55..a89cc83 100644
--- a/src/com/android/settings/network/ProxySubscriptionManager.java
+++ b/src/com/android/settings/network/ProxySubscriptionManager.java
@@ -72,7 +72,7 @@
     private static ProxySubscriptionManager sSingleton;
 
     private ProxySubscriptionManager(Context context) {
-        final Looper looper = Looper.getMainLooper();
+        final Looper looper = context.getMainLooper();
 
         mActiveSubscriptionsListeners =
                 new ArrayList<OnActiveSubscriptionChangedListener>();
diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
new file mode 100644
index 0000000..889fbae
--- /dev/null
+++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 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.settings.network.telephony;
+
+import com.android.settings.dashboard.RestrictedDashboardFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+abstract class AbstractMobileNetworkSettings extends RestrictedDashboardFragment {
+
+    private static final String LOG_TAG = "AbsNetworkSettings";
+
+    /**
+     * @param restrictionKey The restriction key to check before pin protecting
+     *            this settings page. Pass in {@link RESTRICT_IF_OVERRIDABLE} if it should
+     *            be protected whenever a restrictions provider is set. Pass in
+     *            null if it should never be protected.
+     */
+    AbstractMobileNetworkSettings(String restrictionKey) {
+        super(restrictionKey);
+    }
+
+    List<AbstractPreferenceController> getPreferenceControllersAsList() {
+        final List<AbstractPreferenceController> result =
+                new ArrayList<AbstractPreferenceController>();
+        getPreferenceControllers().forEach(controllers -> result.addAll(controllers));
+        return result;
+    }
+
+    TelephonyStatusControlSession setTelephonyAvailabilityStatus(
+            Collection<AbstractPreferenceController> listOfPrefControllers) {
+        return (new TelephonyStatusControlSession.Builder(listOfPrefControllers))
+                .build();
+    }
+
+}
diff --git a/src/com/android/settings/network/telephony/ApnPreferenceController.java b/src/com/android/settings/network/telephony/ApnPreferenceController.java
index dd68129..8442de2 100644
--- a/src/com/android/settings/network/telephony/ApnPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ApnPreferenceController.java
@@ -93,6 +93,9 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
+        if (mPreference == null) {
+            return;
+        }
         ((RestrictedPreference) mPreference).setDisabledByAdmin(
                 MobileNetworkUtils.isDpcApnEnforced(mContext)
                         ? RestrictedLockUtilsInternal.getDeviceOwner(mContext)
diff --git a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
index 650890e..779802a 100644
--- a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
+++ b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java
@@ -35,7 +35,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
 import com.android.settings.network.SubscriptionUtil;
 import com.android.settings.network.SubscriptionsChangeListener;
 
@@ -47,8 +46,8 @@
  * what mobile network subscription is used by default for some service controlled by the
  * SubscriptionManager. This can be used for services such as Calls or SMS.
  */
-public abstract class DefaultSubscriptionController extends BasePreferenceController implements
-        LifecycleObserver, Preference.OnPreferenceChangeListener,
+public abstract class DefaultSubscriptionController extends TelephonyBasePreferenceController
+        implements LifecycleObserver, Preference.OnPreferenceChangeListener,
         SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
     private static final String TAG = "DefaultSubController";
 
@@ -85,7 +84,7 @@
     protected abstract void setDefaultSubscription(int subscriptionId);
 
     @Override
-    public int getAvailabilityStatus() {
+    public int getAvailabilityStatus(int subId) {
         final List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(mManager);
         if (subs.size() > 1) {
             return AVAILABLE;
diff --git a/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java b/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java
index f5bcce7..d14c8d0 100644
--- a/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java
+++ b/src/com/android/settings/network/telephony/DisableSimFooterPreferenceController.java
@@ -20,30 +20,37 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 
-import com.android.settings.core.BasePreferenceController;
 import com.android.settings.network.SubscriptionUtil;
 
-public class DisableSimFooterPreferenceController extends BasePreferenceController {
-    private int mSubId;
+/**
+ * Shows information about disable a physical SIM.
+ */
+public class DisableSimFooterPreferenceController extends TelephonyBasePreferenceController {
 
+    /**
+     * Constructor
+     */
     public DisableSimFooterPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
-        mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     }
 
+    /**
+     * re-init for SIM based on given subscription ID.
+     * @param subId is the given subscription ID
+     */
     public void init(int subId) {
         mSubId = subId;
     }
 
     @Override
-    public int getAvailabilityStatus() {
-        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+    public int getAvailabilityStatus(int subId) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             return CONDITIONALLY_UNAVAILABLE;
         }
 
         SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
         for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
-            if (info.getSubscriptionId() == mSubId) {
+            if (info.getSubscriptionId() == subId) {
                 if (info.isEmbedded() || SubscriptionUtil.showToggleForPhysicalSim(subManager)) {
                     return CONDITIONALLY_UNAVAILABLE;
                 }
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 5343709..d5a192a 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -61,19 +61,6 @@
 
     public EnabledNetworkModePreferenceController(Context context, String key) {
         super(context, key);
-        mPreferredNetworkModeObserver = new PreferredNetworkModeContentObserver(
-                new Handler(Looper.getMainLooper()));
-        mPreferredNetworkModeObserver.setPreferredNetworkModeChangedListener(
-                () -> updatePreference());
-    }
-
-    private void updatePreference() {
-        if (mPreferenceScreen != null) {
-            displayPreference(mPreferenceScreen);
-        }
-        if (mPreference != null) {
-            updateState(mPreference);
-        }
     }
 
     @Override
@@ -100,11 +87,17 @@
 
     @OnLifecycleEvent(ON_START)
     public void onStart() {
+        if (mPreferredNetworkModeObserver == null) {
+            return;
+        }
         mPreferredNetworkModeObserver.register(mContext, mSubId);
     }
 
     @OnLifecycleEvent(ON_STOP)
     public void onStop() {
+        if (mPreferredNetworkModeObserver == null) {
+            return;
+        }
         mPreferredNetworkModeObserver.unregister(mContext);
     }
 
@@ -151,9 +144,25 @@
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
         mBuilder = new PreferenceEntriesBuilder(mContext, mSubId);
 
+        if (mPreferredNetworkModeObserver == null) {
+            mPreferredNetworkModeObserver = new PreferredNetworkModeContentObserver(
+                    new Handler(Looper.getMainLooper()));
+            mPreferredNetworkModeObserver.setPreferredNetworkModeChangedListener(
+                    () -> updatePreference());
+        }
+
         lifecycle.addObserver(this);
     }
 
+    private void updatePreference() {
+        if (mPreferenceScreen != null) {
+            displayPreference(mPreferenceScreen);
+        }
+        if (mPreference != null) {
+            updateState(mPreference);
+        }
+    }
+
     private final static class PreferenceEntriesBuilder {
         private CarrierConfigManager mCarrierConfigManager;
         private Context mContext;
diff --git a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
index 3a91616..91d01d3 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
@@ -69,10 +69,13 @@
     public Enhanced4gBasePreferenceController(Context context, String key) {
         super(context, key);
         m4gLteListeners = new ArrayList<>();
-        mPhoneStateListener = new PhoneCallStateListener();
     }
 
     public Enhanced4gBasePreferenceController init(int subId) {
+        if (mPhoneStateListener == null) {
+            mPhoneStateListener = new PhoneCallStateListener();
+        }
+
         if (mSubId == subId) {
             return this;
         }
@@ -122,11 +125,17 @@
 
     @Override
     public void onStart() {
+        if (mPhoneStateListener == null) {
+            return;
+        }
         mPhoneStateListener.register(mContext, mSubId);
     }
 
     @Override
     public void onStop() {
+        if (mPhoneStateListener == null) {
+            return;
+        }
         mPhoneStateListener.unregister();
     }
 
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 54fe2c1..754dd05 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -36,8 +36,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.dashboard.RestrictedDashboardFragment;
 import com.android.settings.datausage.BillingCyclePreferenceController;
 import com.android.settings.datausage.DataUsageSummaryPreferenceController;
 import com.android.settings.network.telephony.cdma.CdmaSubscriptionPreferenceController;
@@ -47,17 +45,14 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
 
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class MobileNetworkSettings extends RestrictedDashboardFragment {
+public class MobileNetworkSettings extends AbstractMobileNetworkSettings {
 
     private static final String LOG_TAG = "NetworkSettings";
     public static final int REQUEST_CODE_EXIT_ECM = 17;
@@ -133,7 +128,11 @@
     public void onAttach(Context context) {
         super.onAttach(context);
 
-        use(DataUsageSummaryPreferenceController.class).init(mSubId);
+        final DataUsageSummaryPreferenceController dataUsageSummaryPreferenceController =
+                use(DataUsageSummaryPreferenceController.class);
+        if (dataUsageSummaryPreferenceController != null) {
+            dataUsageSummaryPreferenceController.init(mSubId);
+        }
         use(CallsDefaultSubscriptionController.class).init(getLifecycle());
         use(SmsDefaultSubscriptionController.class).init(getLifecycle());
         use(MobileNetworkSwitchController.class).init(getLifecycle(), mSubId);
@@ -189,11 +188,8 @@
     public void onCreate(Bundle icicle) {
         Log.i(LOG_TAG, "onCreate:+");
 
-        final Collection<List<AbstractPreferenceController>> controllerLists =
-                getPreferenceControllers();
-        final Future<Boolean> result = ThreadUtils.postOnBackgroundThread(() ->
-                setupAvailabilityStatus(controllerLists)
-        );
+        final TelephonyStatusControlSession session =
+                setTelephonyAvailabilityStatus(getPreferenceControllersAsList());
 
         super.onCreate(icicle);
         final Context context = getContext();
@@ -201,46 +197,11 @@
         mTelephonyManager = context.getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mSubId);
 
-        //check the background thread is finished then unset the status of availability.
-        try {
-            result.get();
-        } catch (ExecutionException | InterruptedException exception) {
-            Log.e(LOG_TAG, "onCreate, setup availability status failed!", exception);
-        }
-        unsetAvailabilityStatus(controllerLists);
+        session.close();
 
         onRestoreInstance(icicle);
     }
 
-    private Boolean setupAvailabilityStatus(
-            Collection<List<AbstractPreferenceController>> controllerLists) {
-        try {
-            controllerLists.stream().flatMap(Collection::stream)
-                    .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
-                    .map(TelephonyAvailabilityHandler.class::cast)
-                    .forEach(controller -> {
-                        int status = ((BasePreferenceController) controller)
-                                .getAvailabilityStatus();
-                        controller.unsetAvailabilityStatus(true);
-                        controller.setAvailabilityStatus(status);
-                    });
-            return true;
-        } catch (Exception exception) {
-            Log.e(LOG_TAG, "Setup availability status failed!", exception);
-            return false;
-        }
-    }
-
-    private void unsetAvailabilityStatus(
-            Collection<List<AbstractPreferenceController>> controllerLists) {
-        controllerLists.stream().flatMap(Collection::stream)
-                .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
-                .map(TelephonyAvailabilityHandler.class::cast)
-                .forEach(controller -> {
-                    controller.unsetAvailabilityStatus(false);
-                });
-    }
-
     @Override
     public void onExpandButtonClick() {
         final PreferenceScreen screen = getPreferenceScreen();
diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
index 584848f..7877063 100644
--- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java
+++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
@@ -217,16 +217,13 @@
             switch (msg.what) {
                 case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE:
                     final boolean isSucceed = (boolean) msg.obj;
-                    if (isSucceed) {
-                        // Don't enable screen here. Wait until result of network re-scan.
-                        startNetworkQuery();
-                    } else {
-                        stopNetworkQuery();
-                        setProgressBarVisible(false);
-                        getPreferenceScreen().setEnabled(true);
-                        // For failure case, only update the summary of selected item.
-                        mSelectedPreference.setSummary(R.string.network_could_not_connect);
-                    }
+                    stopNetworkQuery();
+                    setProgressBarVisible(false);
+                    getPreferenceScreen().setEnabled(true);
+
+                    mSelectedPreference.setSummary(isSucceed
+                            ? R.string.network_connected
+                            : R.string.network_could_not_connect);
                     break;
                 case EVENT_NETWORK_SCAN_RESULTS:
                     final List<CellInfo> results = (List<CellInfo>) msg.obj;
@@ -418,6 +415,7 @@
                     // (it would be quite confusing why the connected network has no signal)
                     pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1);
                     mPreferenceCategory.addPreference(pref);
+                    break;
                 }
             }
         }
diff --git a/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
index 50dd26b..c1acd91 100644
--- a/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
+++ b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
@@ -25,14 +25,16 @@
 public interface TelephonyAvailabilityHandler {
 
     /**
-     * Set availability to preference controller.
+     * Set availability status of preference controller to a fixed value.
+     * @param status is the given status. Which will be reported from
+     * {@link BasePreferenceController#getAvailabilityStatus()}
      */
-    public void setAvailabilityStatus(int status);
+    void setAvailabilityStatus(int status);
 
     /**
      * Do not set availability, use
      * {@link MobileNetworkUtils#getAvailability(Context, int, TelephonyAvailabilityCallback)}
      * to get the availability.
      */
-    public void unsetAvailabilityStatus(boolean enable);
+    void unsetAvailabilityStatus();
 }
diff --git a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
index 678209d..2bd7de9 100644
--- a/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java
@@ -23,7 +23,6 @@
 
 import com.android.settings.core.BasePreferenceController;
 
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -33,7 +32,7 @@
         implements TelephonyAvailabilityCallback, TelephonyAvailabilityHandler {
     protected int mSubId;
     private AtomicInteger mAvailabilityStatus = new AtomicInteger(0);
-    private AtomicBoolean mUnsetAvailabilityStatus = new AtomicBoolean(false);
+    private AtomicInteger mSetSessionCount = new AtomicInteger(0);
 
     public TelephonyBasePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -42,7 +41,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (!mUnsetAvailabilityStatus.get()) {
+        if (mSetSessionCount.get() <= 0) {
             mAvailabilityStatus.set(MobileNetworkUtils
                     .getAvailability(mContext, mSubId, this::getAvailabilityStatus));
         }
@@ -52,11 +51,12 @@
     @Override
     public void setAvailabilityStatus(int status) {
         mAvailabilityStatus.set(status);
+        mSetSessionCount.getAndIncrement();
     }
 
     @Override
-    public void unsetAvailabilityStatus(boolean enable) {
-        mUnsetAvailabilityStatus.set(enable);
+    public void unsetAvailabilityStatus() {
+        mSetSessionCount.getAndDecrement();
     }
 
     /**
diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
new file mode 100644
index 0000000..12c9bee
--- /dev/null
+++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.settings.network.telephony;
+
+import android.util.Log;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * Session for controlling the status of TelephonyPreferenceController(s).
+ *
+ * Within this session, result of {@link BasePreferenceController#availabilityStatus()}
+ * would be under control.
+ */
+public class TelephonyStatusControlSession implements AutoCloseable {
+
+    private static final String LOG_TAG = "TelephonyStatusControlSS";
+
+    private Collection<AbstractPreferenceController> mControllers;
+    private Future<Boolean> mResult;
+
+    /**
+     * Buider of session
+     */
+    public static class Builder {
+        private Collection<AbstractPreferenceController> mControllers;
+
+        /**
+         * Constructor
+         *
+         * @param controllers is a collection of {@link AbstractPreferenceController}
+         *        which would have {@link BasePreferenceController#availabilityStatus()}
+         *        under control within this session.
+         */
+        public Builder(Collection<AbstractPreferenceController> controllers) {
+            mControllers = controllers;
+        }
+
+        /**
+         * Method to build this session.
+         * @return {@link TelephonyStatusControlSession} session been setup.
+         */
+        public TelephonyStatusControlSession build() {
+            return new TelephonyStatusControlSession(mControllers);
+        }
+    }
+
+    private TelephonyStatusControlSession(Collection<AbstractPreferenceController> controllers) {
+        mControllers = controllers;
+        mResult = ThreadUtils.postOnBackgroundThread(() ->
+            setupAvailabilityStatus(controllers)
+        );
+    }
+
+    /**
+     * Close the session.
+     *
+     * No longer control the status.
+     */
+    public void close() {
+        //check the background thread is finished then unset the status of availability.
+        try {
+            mResult.get();
+        } catch (ExecutionException | InterruptedException exception) {
+            Log.e(LOG_TAG, "setup availability status failed!", exception);
+        }
+        unsetAvailabilityStatus(mControllers);
+    }
+
+    private Boolean setupAvailabilityStatus(
+            Collection<AbstractPreferenceController> controllerLists) {
+        try {
+            controllerLists.stream()
+                    .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
+                    .map(TelephonyAvailabilityHandler.class::cast)
+                    .forEach(controller -> {
+                        int status = ((BasePreferenceController) controller)
+                                .getAvailabilityStatus();
+                        controller.setAvailabilityStatus(status);
+                    });
+            return true;
+        } catch (Exception exception) {
+            Log.e(LOG_TAG, "Setup availability status failed!", exception);
+            return false;
+        }
+    }
+
+    private void unsetAvailabilityStatus(
+            Collection<AbstractPreferenceController> controllerLists) {
+        controllerLists.stream()
+                .filter(controller -> controller instanceof TelephonyAvailabilityHandler)
+                .map(TelephonyAvailabilityHandler.class::cast)
+                .forEach(controller -> {
+                    controller.unsetAvailabilityStatus();
+                });
+    }
+}
diff --git a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
index 56d51eb..84aa0cb 100644
--- a/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
+++ b/src/com/android/settings/network/telephony/TelephonyTogglePreferenceController.java
@@ -23,7 +23,6 @@
 
 import com.android.settings.core.TogglePreferenceController;
 
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -33,8 +32,7 @@
         implements TelephonyAvailabilityCallback, TelephonyAvailabilityHandler {
     protected int mSubId;
     private AtomicInteger mAvailabilityStatus = new AtomicInteger(0);
-    private AtomicBoolean mUnsetAvailabilityStatus = new AtomicBoolean(false);
-
+    private AtomicInteger mSetSessionCount = new AtomicInteger(0);
 
     public TelephonyTogglePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -43,7 +41,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (!mUnsetAvailabilityStatus.get()) {
+        if (mSetSessionCount.get() <= 0) {
             mAvailabilityStatus.set(MobileNetworkUtils
                     .getAvailability(mContext, mSubId, this::getAvailabilityStatus));
         }
@@ -53,11 +51,12 @@
     @Override
     public void setAvailabilityStatus(int status) {
         mAvailabilityStatus.set(status);
+        mSetSessionCount.getAndIncrement();
     }
 
     @Override
-    public void unsetAvailabilityStatus(boolean enable) {
-        mUnsetAvailabilityStatus.set(enable);
+    public void unsetAvailabilityStatus() {
+        mSetSessionCount.getAndDecrement();
     }
 
     /**
diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java
index c36035a..137f780 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.media.RoutingSessionInfo;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
@@ -51,7 +53,7 @@
     static final String SWITCHER_PREFIX = "OUTPUT_SWITCHER";
 
     private PreferenceCategory mPreferenceCategory;
-    private List<MediaDevice> mActiveRemoteMediaDevices = new ArrayList<>();
+    private List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
@@ -67,7 +69,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (mActiveRemoteMediaDevices.isEmpty()) {
+        if (mRoutingSessionInfos.isEmpty()) {
             return CONDITIONALLY_UNAVAILABLE;
         }
         return AVAILABLE_UNSEARCHABLE;
@@ -77,12 +79,19 @@
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mPreferenceCategory = screen.findPreference(getPreferenceKey());
-        mActiveRemoteMediaDevices.clear();
-        mActiveRemoteMediaDevices.addAll(mLocalMediaManager.getActiveMediaDevice(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE));
+        initRemoteMediaSession();
         refreshPreference();
     }
 
+    private void initRemoteMediaSession() {
+        mRoutingSessionInfos.clear();
+        for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
+            if (!info.isSystemSession()) {
+                mRoutingSessionInfos.add(info);
+            }
+        }
+    }
+
     /**
      * onDestroy()
      * {@link androidx.lifecycle.OnLifecycleEvent}
@@ -102,27 +111,27 @@
         final CharSequence outputTitle = mContext.getText(R.string.media_output_title);
         final CharSequence castVolume = mContext.getText(R.string.remote_media_volume_option_title);
         mPreferenceCategory.setVisible(true);
-        int i = 0;
-        for (MediaDevice device : mActiveRemoteMediaDevices) {
-            if (mPreferenceCategory.findPreference(device.getId()) != null) {
+
+        for (RoutingSessionInfo info : mRoutingSessionInfos) {
+            if (mPreferenceCategory.findPreference(info.getId()) != null) {
                 continue;
             }
             // Add slider
             final RemoteVolumeSeekBarPreference seekBarPreference =
                     new RemoteVolumeSeekBarPreference(mContext);
-            seekBarPreference.setKey(device.getId());
-            seekBarPreference.setTitle(castVolume + " (" + device.getClientAppLabel() + ")");
-            seekBarPreference.setMax(device.getMaxVolume());
-            seekBarPreference.setProgress(device.getCurrentVolume());
+            seekBarPreference.setKey(info.getId());
+            seekBarPreference.setTitle(castVolume);
+            seekBarPreference.setMax(info.getVolumeMax());
+            seekBarPreference.setProgress(info.getVolume());
             seekBarPreference.setMin(0);
             seekBarPreference.setOnPreferenceChangeListener(this);
             seekBarPreference.setIcon(R.drawable.ic_volume_remote);
             mPreferenceCategory.addPreference(seekBarPreference);
             // Add output indicator
             final Preference preference = new Preference(mContext);
-            preference.setKey(SWITCHER_PREFIX + device.getId());
+            preference.setKey(SWITCHER_PREFIX + info.getId());
             preference.setTitle(outputTitle);
-            preference.setSummary(device.getName());
+            preference.setSummary(info.getName());
             mPreferenceCategory.addPreference(preference);
         }
     }
@@ -135,7 +144,7 @@
             return false;
         }
         ThreadUtils.postOnBackgroundThread(() -> {
-            device.requestSetVolume((int) newValue);
+            mLocalMediaManager.adjustSessionVolume(preference.getKey(), (int) newValue);
         });
         return true;
     }
@@ -145,18 +154,19 @@
         if (!preference.getKey().startsWith(SWITCHER_PREFIX)) {
             return false;
         }
-        final String key = preference.getKey().substring(SWITCHER_PREFIX.length());
-        final MediaDevice device = mLocalMediaManager.getMediaDeviceById(key);
-        if (device == null) {
-            return false;
+        for (RoutingSessionInfo info : mRoutingSessionInfos) {
+            if (TextUtils.equals(info.getId(),
+                    preference.getKey().substring(SWITCHER_PREFIX.length()))) {
+                final Intent intent = new Intent()
+                        .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                        .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
+                                info.getClientPackageName());
+                mContext.startActivity(intent);
+                return true;
+            }
         }
-        final Intent intent = new Intent()
-                .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
-                        device.getClientPackageName());
-        mContext.startActivity(intent);
-        return true;
+        return false;
     }
 
     @Override
@@ -170,9 +180,7 @@
             // Preference group is not ready.
             return;
         }
-        mActiveRemoteMediaDevices.clear();
-        mActiveRemoteMediaDevices.addAll(mLocalMediaManager.getActiveMediaDevice(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE));
+        initRemoteMediaSession();
         refreshPreference();
     }
 
diff --git a/src/com/android/settings/notification/app/BlockPreferenceController.java b/src/com/android/settings/notification/app/BlockPreferenceController.java
index 37563c0..f55ea8c 100644
--- a/src/com/android/settings/notification/app/BlockPreferenceController.java
+++ b/src/com/android/settings/notification/app/BlockPreferenceController.java
@@ -137,7 +137,7 @@
             } else {
                 fieldContextName = mAppRow.label;
             }
-            return mContext.getString(R.string.notification_switch_label, fieldContextName);
+            return mContext.getString(R.string.notification_app_switch_label, fieldContextName);
         }
     }
 }
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogBaseFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogBaseFragment.java
index eda3204..c17bacd 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogBaseFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogBaseFragment.java
@@ -23,10 +23,13 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
 import java.util.List;
 
 /**
@@ -39,16 +42,7 @@
     final static String EXTRA_APP_NAME = "com.android.settings.wifi.extra.APP_NAME";
 
     NetworkRequestDialogActivity mActivity = null;
-
-    protected String getTitle() {
-        final Intent intent = getActivity().getIntent();
-        String appName = "";
-        if (intent != null) {
-            appName = intent.getStringExtra(EXTRA_APP_NAME);
-        }
-
-        return getString(R.string.network_connection_request_dialog_title, appName);
-    }
+    private String mAppName = "";
 
     @Override
     public int getMetricsCategory() {
@@ -61,6 +55,11 @@
         if (context instanceof NetworkRequestDialogActivity) {
             mActivity = (NetworkRequestDialogActivity) context;
         }
+
+        final Intent intent = getActivity().getIntent();
+        if (intent != null) {
+            mAppName = intent.getStringExtra(EXTRA_APP_NAME);
+        }
     }
 
     @Override
@@ -78,6 +77,14 @@
         }
     }
 
+    protected String getTitle() {
+        return getString(R.string.network_connection_request_dialog_title);
+    }
+
+    protected String getSummary() {
+        return getString(R.string.network_connection_request_dialog_summary, mAppName);
+    }
+
     protected void onUserSelectionCallbackRegistration(
             NetworkRequestUserSelectionCallback userSelectionCallback) {
     }
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
index ee032ed..edaa4d9 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
@@ -86,6 +86,8 @@
 
         final TextView title = customTitle.findViewById(R.id.network_request_title_text);
         title.setText(getTitle());
+        final TextView summary = customTitle.findViewById(R.id.network_request_summary_text);
+        summary.setText(getSummary());
 
         final ProgressBar progressBar = customTitle.findViewById(
                 R.id.network_request_title_progress);
diff --git a/src/com/android/settings/wifi/NetworkRequestSingleSsidDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestSingleSsidDialogFragment.java
index 7a0ccbe..ec91927 100644
--- a/src/com/android/settings/wifi/NetworkRequestSingleSsidDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestSingleSsidDialogFragment.java
@@ -7,7 +7,9 @@
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.TextView;
+
 import androidx.appcompat.app.AlertDialog;
+
 import com.android.settings.R;
 
 /**
@@ -33,6 +35,8 @@
         final View customTitle = inflater.inflate(R.layout.network_request_dialog_title, null);
         final TextView title = customTitle.findViewById(R.id.network_request_title_text);
         title.setText(getTitle());
+        final TextView summary = customTitle.findViewById(R.id.network_request_summary_text);
+        summary.setText(getSummary());
         final ProgressBar progressBar = customTitle
                 .findViewById(R.id.network_request_title_progress);
         progressBar.setVisibility(View.GONE);
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
index 65395a0..b22b156 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
@@ -49,6 +49,7 @@
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.testutils.shadow.ShadowAccountManager;
 import com.android.settings.testutils.shadow.ShadowContentResolver;
+import com.android.settings.testutils.shadow.ShadowSettingsLibUtils;
 import com.android.settingslib.search.SearchIndexableRaw;
 
 import org.junit.After;
@@ -67,7 +68,8 @@
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class})
+@Config(shadows = {ShadowAccountManager.class, ShadowContentResolver.class,
+        ShadowSettingsLibUtils.class})
 public class AccountPreferenceControllerTest {
 
     @Mock(answer = RETURNS_DEEP_STUBS)
@@ -95,7 +97,7 @@
         when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
         when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
         when(mAccountManager.getAuthenticatorTypesAsUser(anyInt()))
-            .thenReturn(new AuthenticatorDescription[0]);
+                .thenReturn(new AuthenticatorDescription[0]);
         when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
         mController = new AccountPreferenceController(mContext, mFragment, null, mAccountHelper,
                 ProfileSelectFragment.ProfileType.ALL);
@@ -341,8 +343,8 @@
         when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(accounts);
 
         Account[] accountType1 = {
-            new Account("Account11", "com.acct1"),
-            new Account("Account12", "com.acct1")
+                new Account("Account11", "com.acct1"),
+                new Account("Account12", "com.acct1")
         };
         when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct1"), any(UserHandle.class)))
                 .thenReturn(accountType1);
@@ -535,8 +537,8 @@
         when(mAccountManager.getAccountsAsUser(anyInt())).thenReturn(accounts);
 
         Account[] accountType1 = {
-            new Account("Acct11", "com.acct1"),
-            new Account("Acct12", "com.acct1")
+                new Account("Acct11", "com.acct1"),
+                new Account("Acct12", "com.acct1")
         };
         when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct1"), any(UserHandle.class)))
                 .thenReturn(accountType1);
@@ -555,7 +557,7 @@
         mController.onResume();
 
         // remove an account
-        accountType1 = new Account[] {new Account("Acct11", "com.acct1")};
+        accountType1 = new Account[]{new Account("Acct11", "com.acct1")};
         when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct1"), any(UserHandle.class)))
                 .thenReturn(accountType1);
 
@@ -577,19 +579,19 @@
         Account[] accounts = {new Account("Acct1", "com.acct1")};
         when(mAccountManager.getAccountsAsUser(1)).thenReturn(accounts);
         when(mAccountManager.getAccountsByTypeAsUser(eq("com.acct1"), any(UserHandle.class)))
-            .thenReturn(accounts);
+                .thenReturn(accounts);
 
         AuthenticatorDescription[] authDescs = {
-            new AuthenticatorDescription("com.acct1", "com.android.settings",
-                R.string.account_settings_title, 0 /* iconId */, 0 /* smallIconId */,
-                0 /* prefId */, false /* customTokens */)
+                new AuthenticatorDescription("com.acct1", "com.android.settings",
+                        R.string.account_settings_title, 0 /* iconId */, 0 /* smallIconId */,
+                        0 /* prefId */, false /* customTokens */)
         };
         when(mAccountManager.getAuthenticatorTypesAsUser(anyInt())).thenReturn(authDescs);
 
         AccessiblePreferenceCategory preferenceGroup = mock(AccessiblePreferenceCategory.class);
         when(preferenceGroup.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
         when(mAccountHelper.createAccessiblePreferenceCategory(any(Context.class)))
-            .thenReturn(preferenceGroup);
+                .thenReturn(preferenceGroup);
 
         // First time resume will build the UI with no account
         mController.onResume();
diff --git a/tests/robotests/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceControllerTest.java
new file mode 100644
index 0000000..10eed2f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/QuickSettingsMediaPlayerPreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 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.settings.development;
+
+import static com.android.settings.development.QuickSettingsMediaPlayerPreferenceController.SETTING_NAME;
+import static com.android.settings.development.QuickSettingsMediaPlayerPreferenceController.SETTING_VALUE_OFF;
+import static com.android.settings.development.QuickSettingsMediaPlayerPreferenceController.SETTING_VALUE_ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class QuickSettingsMediaPlayerPreferenceControllerTest {
+    @Mock
+    private SwitchPreference mPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private Context mContext;
+    private QuickSettingsMediaPlayerPreferenceController mController;
+
+    @Before
+    public void setup() {
+        mContext = RuntimeEnvironment.application;
+        mController = new QuickSettingsMediaPlayerPreferenceController(mContext);
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+            .thenReturn(mPreference);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void onPreferenceChanged_turnOnPreference_shouldEnable() {
+        mController.onPreferenceChange(mPreference, true /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                SETTING_NAME, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_ON);
+    }
+
+    @Test
+    public void onPreferenceChanged_turnOffPreference_shouldDisable() {
+        mController.onPreferenceChange(mPreference, false /* new value */);
+
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                SETTING_NAME, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+    }
+
+    @Test
+    public void updateState_settingEnabled_preferenceShouldBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(), SETTING_NAME, SETTING_VALUE_ON);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(true);
+    }
+
+    @Test
+    public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
+        Settings.Global.putInt(mContext.getContentResolver(), SETTING_NAME, SETTING_VALUE_OFF);
+        mController.updateState(mPreference);
+
+        verify(mPreference).setChecked(false);
+    }
+
+    @Test
+    public void onDeveloperOptionsSwitchDisabled_shouldDisable() {
+        mController.onDeveloperOptionsSwitchDisabled();
+        final int mode = Settings.Global.getInt(mContext.getContentResolver(),
+                SETTING_NAME, -1 /* default */);
+
+        assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
+        verify(mPreference).setEnabled(false);
+        verify(mPreference).setChecked(false);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
index 8014e56..e8f7027 100644
--- a/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/media/MediaDeviceUpdateWorkerTest.java
@@ -31,6 +31,7 @@
 import android.content.Intent;
 import android.media.AudioManager;
 import android.media.MediaRoute2ProviderService;
+import android.media.RoutingSessionInfo;
 import android.net.Uri;
 
 import com.android.settings.testutils.shadow.ShadowAudioManager;
@@ -191,4 +192,21 @@
 
         verify(mResolver, never()).notifyChange(URI, null);
     }
+
+    @Test
+    public void getActiveRemoteMediaSession_verifyList() {
+        mMediaDeviceUpdateWorker.mLocalMediaManager = mock(LocalMediaManager.class);
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo remoteSessionInfo = mock(RoutingSessionInfo.class);
+        final RoutingSessionInfo localSessionInfo = mock(RoutingSessionInfo.class);
+        when(remoteSessionInfo.isSystemSession()).thenReturn(false);
+        when(localSessionInfo.isSystemSession()).thenReturn(true);
+        routingSessionInfos.add(remoteSessionInfo);
+        routingSessionInfos.add(localSessionInfo);
+        when(mMediaDeviceUpdateWorker.mLocalMediaManager.getActiveMediaSession()).thenReturn(
+                routingSessionInfos);
+
+        assertThat(mMediaDeviceUpdateWorker.getActiveRemoteMediaDevice()).containsExactly(
+                remoteSessionInfo);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
index b719a9e..017faa5 100644
--- a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
@@ -24,7 +24,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -32,6 +32,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.media.RoutingSessionInfo;
 import android.net.Uri;
 
 import androidx.slice.Slice;
@@ -43,7 +44,6 @@
 
 import com.android.settings.slices.SliceBackgroundWorker;
 import com.android.settingslib.media.LocalMediaManager;
-import com.android.settingslib.media.MediaDevice;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -64,19 +64,16 @@
 public class RemoteMediaSliceTest {
 
     private static final String MEDIA_ID = "media_id";
-    private static final String TEST_PACKAGE_LABEL = "music";
-    private static final String TEST_DEVICE_1_ID = "test_device_1_id";
-    private static final String TEST_DEVICE_1_NAME = "test_device_1_name";
+    private static final String TEST_SESSION_1_ID = "test_session_1_id";
+    private static final String TEST_SESSION_1_NAME = "test_session_1_name";
     private static final int TEST_VOLUME = 3;
 
     private static MediaDeviceUpdateWorker sMediaDeviceUpdateWorker;
 
     @Mock
     private LocalMediaManager mLocalMediaManager;
-    @Mock
-    private MediaDevice mDevice;
 
-    private final List<MediaDevice> mDevices = new ArrayList<>();
+    private final List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
     private Context mContext;
     private RemoteMediaSlice mRemoteMediaSlice;
@@ -93,44 +90,42 @@
         sMediaDeviceUpdateWorker = spy(new MediaDeviceUpdateWorker(mContext,
                 REMOTE_MEDIA_SLICE_URI));
         sMediaDeviceUpdateWorker.mLocalMediaManager = mLocalMediaManager;
-        when(sMediaDeviceUpdateWorker.getActiveMediaDevice(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE)).thenReturn(mDevices);
-        when(mDevice.getId()).thenReturn(TEST_DEVICE_1_ID);
-        when(mDevice.getName()).thenReturn(TEST_DEVICE_1_NAME);
-        when(mDevice.getMaxVolume()).thenReturn(100);
-        when(mDevice.getCurrentVolume()).thenReturn(10);
-        when(mDevice.getClientAppLabel()).thenReturn(TEST_PACKAGE_LABEL);
+        final RoutingSessionInfo remoteSessionInfo = mock(RoutingSessionInfo.class);
+        when(remoteSessionInfo.getId()).thenReturn(TEST_SESSION_1_ID);
+        when(remoteSessionInfo.getName()).thenReturn(TEST_SESSION_1_NAME);
+        when(remoteSessionInfo.getVolumeMax()).thenReturn(100);
+        when(remoteSessionInfo.getVolume()).thenReturn(10);
+        when(remoteSessionInfo.isSystemSession()).thenReturn(false);
+        mRoutingSessionInfos.add(remoteSessionInfo);
+        when(sMediaDeviceUpdateWorker.getActiveRemoteMediaDevice()).thenReturn(
+                mRoutingSessionInfos);
     }
 
     @Test
     public void onNotifyChange_noId_doNothing() {
-        mDevices.add(mDevice);
-        when(mLocalMediaManager.getMediaDeviceById(mDevices, TEST_DEVICE_1_ID)).thenReturn(mDevice);
-        sMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices);
         final Intent intent = new Intent();
         intent.putExtra(EXTRA_RANGE_VALUE, TEST_VOLUME);
 
         mRemoteMediaSlice.onNotifyChange(intent);
 
-        verify(mDevice, never()).requestSetVolume(anyInt());
+        verify(sMediaDeviceUpdateWorker, never())
+                .adjustSessionVolume(TEST_SESSION_1_ID, TEST_VOLUME);
     }
 
     @Test
     public void onNotifyChange_verifyAdjustVolume() {
-        mDevices.add(mDevice);
-        when(mLocalMediaManager.getMediaDeviceById(mDevices, TEST_DEVICE_1_ID)).thenReturn(mDevice);
-        sMediaDeviceUpdateWorker.onDeviceListUpdate(mDevices);
         final Intent intent = new Intent();
-        intent.putExtra(MEDIA_ID, TEST_DEVICE_1_ID);
+        intent.putExtra(MEDIA_ID, TEST_SESSION_1_ID);
         intent.putExtra(EXTRA_RANGE_VALUE, TEST_VOLUME);
 
         mRemoteMediaSlice.onNotifyChange(intent);
 
-        verify(mDevice).requestSetVolume(TEST_VOLUME);
+        verify(sMediaDeviceUpdateWorker).adjustSessionVolume(TEST_SESSION_1_ID, TEST_VOLUME);
     }
 
     @Test
-    public void getSlice_noActiveDevice_checkRowNumber() {
+    public void getSlice_noActiveSession_checkRowNumber() {
+        mRoutingSessionInfos.clear();
         final Slice slice = mRemoteMediaSlice.getSlice();
         final int rows = SliceQuery.findAll(slice, FORMAT_SLICE, HINT_LIST_ITEM, null).size();
 
@@ -138,8 +133,7 @@
     }
 
     @Test
-    public void getSlice_withActiveDevice_checkRowNumber() {
-        mDevices.add(mDevice);
+    public void getSlice_withActiveSession_checkRowNumber() {
         final Slice slice = mRemoteMediaSlice.getSlice();
         final int rows = SliceQuery.findAll(slice, FORMAT_SLICE, HINT_LIST_ITEM, null).size();
 
@@ -148,15 +142,13 @@
     }
 
     @Test
-    public void getSlice_withActiveDevice_checkTitle() {
-        mDevices.add(mDevice);
+    public void getSlice_withActiveSession_checkTitle() {
         final Slice slice = mRemoteMediaSlice.getSlice();
         final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
         final SliceAction primaryAction = metadata.getPrimaryAction();
 
         assertThat(primaryAction.getTitle().toString()).isEqualTo(mContext.getText(
-                com.android.settings.R.string.remote_media_volume_option_title)
-                + " (" + TEST_PACKAGE_LABEL + ")");
+                com.android.settings.R.string.remote_media_volume_option_title));
     }
 
     @Implements(SliceBackgroundWorker.class)
diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
index d81f30f..9e140ce 100644
--- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
@@ -21,11 +21,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.media.RoutingSessionInfo;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
@@ -36,7 +38,6 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settings.widget.SeekBarPreference;
 import com.android.settingslib.media.LocalMediaManager;
-import com.android.settingslib.media.MediaDevice;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -55,24 +56,21 @@
 public class RemoteVolumeGroupControllerTest {
 
     private static final String KEY_REMOTE_VOLUME_GROUP = "remote_media_group";
-    private static final String TEST_PACKAGE_LABEL = "music";
-    private static final String TEST_DEVICE_1_ID = "test_device_1_id";
-    private static final String TEST_DEVICE_1_NAME = "test_device_1_name";
+    private static final String TEST_SESSION_1_ID = "test_session_1_id";
+    private static final String TEST_SESSION_1_NAME = "test_session_1_name";
     private static final int CURRENT_VOLUME = 30;
     private static final int MAX_VOLUME = 100;
 
     @Mock
     private LocalMediaManager mLocalMediaManager;
     @Mock
-    private MediaDevice mDevice;
-    @Mock
     private PreferenceScreen mScreen;
     @Mock
     private PreferenceManager mPreferenceManager;
     @Mock
     private SharedPreferences mSharedPreferences;
 
-    private final List<MediaDevice> mDevices = new ArrayList<>();
+    private final List<RoutingSessionInfo> mRoutingSessionInfos = new ArrayList<>();
 
     private Context mContext;
     private RemoteVolumeGroupController mController;
@@ -89,92 +87,88 @@
 
         when(mPreferenceCategory.getPreferenceManager()).thenReturn(mPreferenceManager);
         when(mPreferenceManager.getSharedPreferences()).thenReturn(mSharedPreferences);
-        when(mLocalMediaManager.getActiveMediaDevice(
-                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE)).thenReturn(mDevices);
-        when(mDevice.getId()).thenReturn(TEST_DEVICE_1_ID);
-        when(mDevice.getName()).thenReturn(TEST_DEVICE_1_NAME);
-        when(mDevice.getMaxVolume()).thenReturn(MAX_VOLUME);
-        when(mDevice.getCurrentVolume()).thenReturn(CURRENT_VOLUME);
-        when(mDevice.getClientAppLabel()).thenReturn(TEST_PACKAGE_LABEL);
         when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(
                 mPreferenceCategory);
+        final RoutingSessionInfo remoteSessionInfo = mock(RoutingSessionInfo.class);
+        when(remoteSessionInfo.getId()).thenReturn(TEST_SESSION_1_ID);
+        when(remoteSessionInfo.getName()).thenReturn(TEST_SESSION_1_NAME);
+        when(remoteSessionInfo.getVolumeMax()).thenReturn(MAX_VOLUME);
+        when(remoteSessionInfo.getVolume()).thenReturn(CURRENT_VOLUME);
+        when(remoteSessionInfo.isSystemSession()).thenReturn(false);
+        mRoutingSessionInfos.add(remoteSessionInfo);
+        when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
     }
 
     @Test
-    public void getAvailabilityStatus_withActiveDevice_returnAvailableUnsearchable() {
-        mDevices.add(mDevice);
+    public void getAvailabilityStatus_withActiveSession_returnAvailableUnsearchable() {
         mController.displayPreference(mScreen);
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
     }
 
     @Test
-    public void getAvailabilityStatus_noActiveDevice_returnConditionallyUnavailable() {
+    public void getAvailabilityStatus_noActiveSession_returnConditionallyUnavailable() {
+        mRoutingSessionInfos.clear();
         mController.displayPreference(mScreen);
 
         assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
     }
 
     @Test
-    public void displayPreference_noActiveDevice_checkPreferenceCount() {
+    public void displayPreference_noActiveSession_checkPreferenceCount() {
+        mRoutingSessionInfos.clear();
         mController.displayPreference(mScreen);
 
         assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkPreferenceCount() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkPreferenceCount() {
         mController.displayPreference(mScreen);
 
         assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkSeekBarTitle() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkSeekBarTitle() {
         mController.displayPreference(mScreen);
-        final Preference preference = mPreferenceCategory.findPreference(TEST_DEVICE_1_ID);
+        final Preference preference = mPreferenceCategory.findPreference(TEST_SESSION_1_ID);
 
         assertThat(preference.getTitle()).isEqualTo(mContext.getText(
-                R.string.remote_media_volume_option_title) + " (" + TEST_PACKAGE_LABEL + ")");
+                R.string.remote_media_volume_option_title));
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkSeekBarMaxVolume() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkSeekBarMaxVolume() {
         mController.displayPreference(mScreen);
-        final SeekBarPreference preference = mPreferenceCategory.findPreference(TEST_DEVICE_1_ID);
+        final SeekBarPreference preference = mPreferenceCategory.findPreference(TEST_SESSION_1_ID);
 
         assertThat(preference.getMax()).isEqualTo(MAX_VOLUME);
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkSeekBarCurrentVolume() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkSeekBarCurrentVolume() {
         mController.displayPreference(mScreen);
-        final SeekBarPreference preference = mPreferenceCategory.findPreference(TEST_DEVICE_1_ID);
+        final SeekBarPreference preference = mPreferenceCategory.findPreference(TEST_SESSION_1_ID);
 
         assertThat(preference.getProgress()).isEqualTo(CURRENT_VOLUME);
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkSwitcherPreferenceTitle() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkSwitcherPreferenceTitle() {
         mController.displayPreference(mScreen);
         final Preference preference = mPreferenceCategory.findPreference(
-                RemoteVolumeGroupController.SWITCHER_PREFIX + TEST_DEVICE_1_ID);
+                RemoteVolumeGroupController.SWITCHER_PREFIX + TEST_SESSION_1_ID);
 
         assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.media_output_title));
     }
 
     @Test
-    public void displayPreference_withActiveDevice_checkSwitcherPreferenceSummary() {
-        mDevices.add(mDevice);
+    public void displayPreference_withActiveSession_checkSwitcherPreferenceSummary() {
         mController.displayPreference(mScreen);
         final Preference preference = mPreferenceCategory.findPreference(
-                RemoteVolumeGroupController.SWITCHER_PREFIX + TEST_DEVICE_1_ID);
+                RemoteVolumeGroupController.SWITCHER_PREFIX + TEST_SESSION_1_ID);
 
-        assertThat(preference.getSummary()).isEqualTo(TEST_DEVICE_1_NAME);
+        assertThat(preference.getSummary()).isEqualTo(TEST_SESSION_1_NAME);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsLibUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsLibUtils.java
index 2fce5ad..8f4b786 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsLibUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowSettingsLibUtils.java
@@ -20,6 +20,7 @@
 import android.content.pm.ApplicationInfo;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
 
 import com.android.settingslib.Utils;
 
@@ -30,6 +31,11 @@
 public class ShadowSettingsLibUtils {
 
     @Implementation
+    protected static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) {
+        return new ColorDrawable(0);
+    }
+
+    @Implementation
     protected static Drawable getBadgedIcon(Context context, ApplicationInfo appInfo) {
         return new ColorDrawable(0);
     }