Merge "Add support for @EnabledSince compat changes" am: 2d57867aba am: 38747f76f3 am: 8e111e3de3

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/1455019

Change-Id: Iec4b14d29945ae3088838428cc48b36ae71faeb2
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 969f68d..f2c540f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1921,6 +1921,7 @@
             <intent-filter android:priority="1">
                 <action android:name="android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
                 <action android:name="com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
+                <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
@@ -3257,6 +3258,11 @@
             android:permission="android.permission.MANAGE_SLICE_PERMISSIONS"
             android:exported="true" />
 
+        <receiver
+            android:name=".slices.VolumeSliceRelayReceiver"
+            android:permission="android.permission.MANAGE_SLICE_PERMISSIONS"
+            android:exported="true" />
+
         <!-- Couldn't be triggered from outside of settings. Statsd can trigger it because we send
              PendingIntent to it-->
         <receiver android:name=".fuelgauge.batterytip.AnomalyDetectionReceiver"
diff --git a/res/drawable/ic_illustration_adaptive_connectivity.xml b/res/drawable/ic_illustration_adaptive_connectivity.xml
new file mode 100644
index 0000000..748dd66
--- /dev/null
+++ b/res/drawable/ic_illustration_adaptive_connectivity.xml
@@ -0,0 +1,99 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="412dp"
+    android:height="300dp"
+    android:viewportWidth="412"
+    android:viewportHeight="300">
+  <path
+      android:pathData="M0,0h412v300h-412z"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M206,150m-118,0a118,118 0,1 1,236 0a118,118 0,1 1,-236 0"
+      android:strokeAlpha="0.2"
+      android:fillColor="#8ab4f8"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M206,150m-101.17,0a101.17,101.17 0,1 1,202.34 0a101.17,101.17 0,1 1,-202.34 0"
+      android:strokeAlpha="0.2"
+      android:fillColor="#8ab4f8"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M206,150m-84.44,0a84.44,84.44 0,1 1,168.88 0a84.44,84.44 0,1 1,-168.88 0"
+      android:strokeAlpha="0.2"
+      android:fillColor="#8ab4f8"
+      android:fillAlpha="0.2"/>
+  <path
+      android:pathData="M141.15,25.48h129.87v250.83h-129.87z"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M199.19,237.66h15.2a4.83,4.83 0,0 1,4.83 4.83h0a4.84,4.84 0,0 1,-4.83 4.84h-15.2a4.84,4.84 0,0 1,-4.83 -4.84h0A4.83,4.83 0,0 1,199.19 237.66Z"
+      android:fillColor="#d2e3fc"/>
+  <path
+      android:pathData="M212.32,235.59h0a6.9,6.9 0,0 1,6.9 6.9h0a6.9,6.9 0,0 1,-6.9 6.91h0a6.91,6.91 0,0 1,-6.91 -6.91h0A6.9,6.9 0,0 1,212.32 235.59Z"
+      android:fillColor="#1a73e8"/>
+  <path
+      android:pathData="M262.43,25.48a5,5 0,0 1,5 5V269.54a5,5 0,0 1,-5 5H147.87a5,5 0,0 1,-5 -5V30.46a5,5 0,0 1,5 -5H262.43m0,-6.23H147.87a11.22,11.22 0,0 0,-11.2 11.21V269.54a11.22,11.22 0,0 0,11.2 11.21H262.43a11.22,11.22 0,0 0,11.21 -11.21V30.46a11.22,11.22 0,0 0,-11.21 -11.21Z"
+      android:fillColor="#f1f3f4"/>
+  <path
+      android:pathData="M262.43,282H147.87a12.46,12.46 0,0 1,-12.45 -12.45V30.46A12.46,12.46 0,0 1,147.87 18H262.43a12.46,12.46 0,0 1,12.45 12.45V269.54A12.46,12.46 0,0 1,262.43 282ZM147.87,20.5a10,10 0,0 0,-10 10V269.54a10,10 0,0 0,10 10H262.43a10,10 0,0 0,10 -10V30.46a10,10 0,0 0,-10 -10Z"
+      android:fillColor="#dadce0"/>
+  <path
+      android:pathData="M274.88,82.76h0.69a1.8,1.8 0,0 1,1.8 1.8V95.9a1.8,1.8 0,0 1,-1.8 1.8h-0.69a0,0 0,0 1,0 0V82.76A0,0 0,0 1,274.88 82.76Z"
+      android:fillColor="#dadce0"/>
+  <path
+      android:pathData="M274.88,117.62h0.69a1.8,1.8 0,0 1,1.8 1.8v26.28a1.8,1.8 0,0 1,-1.8 1.8h-0.69a0,0 0,0 1,0 0V117.62a0,0 0,0 1,0 0Z"
+      android:fillColor="#dadce0"/>
+  <path
+      android:pathData="M206.64,182.08m-16.56,0a16.56,16.56 0,1 1,33.12 0a16.56,16.56 0,1 1,-33.12 0"
+      android:fillColor="#f1f3f4"/>
+  <path
+      android:pathData="M190.08,182.08a16.56,16.53 0,1 0,33.12 0a16.56,16.53 0,1 0,-33.12 0z"
+      android:fillColor="#fdd663"/>
+  <path
+      android:pathData="M206.64,174.54l-6.21,4.66l0,9.32l3.88,0l0,-5.44l4.66,0l0,5.44l3.88,0l0,-9.32l-6.21,-4.66z"
+      android:fillColor="#f29900"/>
+  <path
+      android:pathData="M165.84,101.31m-16.53,0a16.53,16.53 0,1 1,33.06 0a16.53,16.53 0,1 1,-33.06 0"
+      android:fillColor="#f6aea9"/>
+  <path
+      android:pathData="M169.77,108.12V96.07a1,1 0,0 0,-1 -1h-1.32V93.46h-3.14V95H163a1,1 0,0 0,-1 1v12a1,1 0,0 0,1 1h5.76A1,1 0,0 0,169.77 108.12ZM168.2,96.6v5.64h-4.71V96.6Z"
+      android:fillColor="#ea4335"/>
+  <path
+      android:pathData="M246.16,152.08m-16.56,0a16.56,16.56 0,1 1,33.12 0a16.56,16.56 0,1 1,-33.12 0"
+      android:fillColor="#f6aea9"/>
+  <path
+      android:pathData="M251.88,146a1.18,1.18 0,0 0,-1.12 -0.79h-8.63A1.19,1.19 0,0 0,241 146l-1.63,4.7L239.37,157a0.78,0.78 0,0 0,0.78 0.79L241,157.79a0.78,0.78 0,0 0,0.78 -0.79v-0.78h9.42L251.2,157a0.79,0.79 0,0 0,0.79 0.79h0.78a0.79,0.79 0,0 0,0.79 -0.79v-6.28ZM242.88,153.84a1.18,1.18 0,1 1,1.18 -1.17A1.17,1.17 0,0 1,242.91 153.88ZM249.95,153.84a1.18,1.18 0,1 1,1.17 -1.17A1.17,1.17 0,0 1,250 153.88ZM241.52,149.22 L242.37,146.78h8.08l0.85,2.44Z"
+      android:fillColor="#ea4335"/>
+  <path
+      android:pathData="M165.84,152.08m-16.56,0a16.56,16.56 0,1 1,33.12 0a16.56,16.56 0,1 1,-33.12 0"
+      android:fillColor="#aecbfa"/>
+  <path
+      android:pathData="M173.24,149.16a4,4 0,0 0,-4 -3.34h-6.11a4.06,4.06 0,0 0,-4 3.34s-1.27,7.34 -1.27,7.45a1.73,1.73 0,0 0,1.73 1.73,1.7 1.7,0 0,0 1.22,-0.49l2.89,-2.85h5l2.88,2.85a1.71,1.71 0,0 0,1.23 0.49,1.73 1.73,0 0,0 1.73,-1.73C174.52,156.5 173.24,149.16 173.24,149.16ZM165.34,150.83h-1.67v1.67h-0.84v-1.67h-1.66L161.17,150h1.66v-1.67h0.84L163.67,150h1.67ZM167.68,151.08a0.67,0.67 0,1 1,0.66 -0.67A0.67,0.67 0,0 1,167.68 151.08ZM169.09,152.5a0.67,0.67 0,1 1,0.66 -0.67A0.67,0.67 0,0 1,169.09 152.5ZM169.09,149.66a0.67,0.67 0,1 1,0.66 -0.67A0.67,0.67 0,0 1,169.09 149.66ZM170.51,151.08a0.67,0.67 0,1 1,0.67 -0.67A0.67,0.67 0,0 1,170.51 151.08Z"
+      android:fillColor="#4285f4"/>
+  <path
+      android:pathData="M246.16,101.31m-16.53,0a16.53,16.53 0,1 1,33.06 0a16.53,16.53 0,1 1,-33.06 0"
+      android:fillColor="#aecbfa"/>
+  <path
+      android:pathData="M242.91,96.47a4.51,4.51 0,0 0,0 6.49l0.83,-0.83a3.47,3.47 0,0 1,-1 -2.42,3.23 3.23,0 0,1 1,-2.41ZM251.06,94.81 L250.23,95.64a5.84,5.84 0,0 1,0 8.14l0.83,0.83a6.77,6.77 0,0 0,2 -4.9A7.08,7.08 0,0 0,251.06 94.81ZM242.06,95.64 L241.24,94.81a7,7 0,0 0,0 9.8l0.82,-0.83a5.84,5.84 0,0 1,0 -8.14ZM249.38,96.47 L248.55,97.3a3.46,3.46 0,0 1,1 2.41,3.25 3.25,0 0,1 -1,2.42l0.83,0.83a4.61,4.61 0,0 0,1.38 -3.25A4.92,4.92 0,0 0,249.4 96.47ZM246.16,98a1.71,1.71 0,0 0,-1.73 1.72,1.76 1.76,0 0,0 1,1.59v6h1.38v-6a1.75,1.75 0,0 0,1 -1.59A1.71,1.71 0,0 0,246.16 98Z"
+      android:fillColor="#1a73e8"/>
+  <path
+      android:pathData="M206.64,71.31m-16.5,0a16.5,16.5 0,1 1,33 0a16.5,16.5 0,1 1,-33 0"
+      android:fillColor="#a8dab5"/>
+  <path
+      android:pathData="M212.14,65.81h-2.06l1.38,2.75h-2.07L208,65.81h-1.38L208,68.56L206,68.56l-1.37,-2.75L203.2,65.81l1.38,2.75h-2.06l-1.38,-2.75a1.37,1.37 0,0 0,-1.37 1.38v8.25a1.37,1.37 0,0 0,1.37 1.37h11a1.38,1.38 0,0 0,1.38 -1.37L213.52,67.19A1.38,1.38 0,0 0,212.14 65.81ZM212.14,75.44h-11v-5.5h11Z"
+      android:fillColor="#34a853"/>
+</vector>
diff --git a/res/layout/adaptive_connectivity_header.xml b/res/layout/adaptive_connectivity_header.xml
new file mode 100644
index 0000000..4c5fb0c
--- /dev/null
+++ b/res/layout/adaptive_connectivity_header.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:contentDescription="@null"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_illustration_adaptive_connectivity"/>
+
+</FrameLayout>
diff --git a/res/layout/adb_qrcode_scanner_fragment.xml b/res/layout/adb_qrcode_scanner_fragment.xml
index f37c9a6..9a337d9 100644
--- a/res/layout/adb_qrcode_scanner_fragment.xml
+++ b/res/layout/adb_qrcode_scanner_fragment.xml
@@ -19,7 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:theme="@style/GlifV3Theme.Light"
+    android:theme="@style/GlifV3Theme"
     android:icon="@drawable/ic_scan_32dp">
 
     <LinearLayout
diff --git a/res/layout/bubble_preference.xml b/res/layout/bubble_preference.xml
index 8a64716..08f25b4 100644
--- a/res/layout/bubble_preference.xml
+++ b/res/layout/bubble_preference.xml
@@ -44,7 +44,7 @@
             android:id="@+id/bubble_all_icon"
             android:src="@drawable/ic_bubble_all"
             android:background="@android:color/transparent"
-            android:layout_gravity="center"
+            android:layout_centerVertical="true"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:clickable="false"
@@ -53,6 +53,7 @@
             android:id="@+id/bubble_all_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
             android:ellipsize="end"
             android:maxLines="2"
             android:clickable="false"
@@ -75,7 +76,7 @@
             android:id="@+id/bubble_selected_icon"
             android:src="@drawable/ic_bubble_selected"
             android:background="@android:color/transparent"
-            android:layout_gravity="center"
+            android:layout_centerVertical="true"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:clickable="false"
@@ -84,6 +85,7 @@
             android:id="@+id/bubble_selected_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
             android:ellipsize="end"
             android:maxLines="2"
             android:clickable="false"
@@ -106,7 +108,7 @@
             android:id="@+id/bubble_none_icon"
             android:src="@drawable/ic_bubble_none"
             android:background="@android:color/transparent"
-            android:layout_gravity="center"
+            android:layout_centerVertical="true"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:clickable="false"
@@ -115,6 +117,7 @@
             android:id="@+id/bubble_none_label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
             android:ellipsize="end"
             android:maxLines="2"
             android:clickable="false"
@@ -125,4 +128,4 @@
             android:text="@string/bubble_app_setting_none"/>
     </com.android.settings.notification.NotificationButtonRelativeLayout>
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/layout/notif_priority_conversation_preference.xml b/res/layout/notif_priority_conversation_preference.xml
index f68dbde..9c1a302 100644
--- a/res/layout/notif_priority_conversation_preference.xml
+++ b/res/layout/notif_priority_conversation_preference.xml
@@ -32,7 +32,7 @@
         android:clickable="true"
         android:focusable="true">
         <ImageView
-            android:id="@+id/priority_icon"
+            android:id="@+id/icon"
             android:src="@drawable/ic_important_outline"
             android:background="@android:color/transparent"
             android:layout_gravity="center"
@@ -41,19 +41,19 @@
             android:clickable="false"
             android:focusable="false"/>
         <TextView
-            android:id="@+id/priority_label"
+            android:id="@+id/label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
             android:clickable="false"
             android:focusable="false"
-            android:layout_toEndOf="@id/priority_icon"
+            android:layout_toEndOf="@id/icon"
             android:layout_marginStart="@dimen/notification_importance_drawable_padding"
             android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
             android:text="@string/notification_priority_title"/>
         <TextView
-            android:id="@+id/priority_summary"
+            android:id="@+id/summary"
             android:paddingTop="@dimen/notification_importance_button_padding"
             android:text="@string/notification_channel_summary_priority"
             android:layout_width="match_parent"
@@ -62,7 +62,7 @@
             android:focusable="false"
             android:ellipsize="end"
             android:maxLines="3"
-            android:layout_below="@id/priority_icon"
+            android:layout_below="@id/icon"
             android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
             android:visibility="gone" />
     </com.android.settings.notification.NotificationButtonRelativeLayout>
@@ -76,7 +76,7 @@
         android:clickable="true"
         android:focusable="true">
         <ImageView
-            android:id="@+id/alert_icon"
+            android:id="@+id/icon"
             android:src="@drawable/ic_notifications_alert"
             android:background="@android:color/transparent"
             android:layout_gravity="center"
@@ -85,19 +85,19 @@
             android:clickable="false"
             android:focusable="false"/>
         <TextView
-            android:id="@+id/alert_label"
+            android:id="@+id/label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
             android:clickable="false"
             android:focusable="false"
-            android:layout_toEndOf="@id/alert_icon"
+            android:layout_toEndOf="@id/icon"
             android:layout_marginStart="@dimen/notification_importance_drawable_padding"
             android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
             android:text="@string/notification_alert_title"/>
         <TextView
-            android:id="@+id/alert_summary"
+            android:id="@+id/summary"
             android:paddingTop="@dimen/notification_importance_button_padding"
             android:text="@string/notification_channel_summary_default"
             android:layout_width="match_parent"
@@ -106,7 +106,7 @@
             android:focusable="false"
             android:ellipsize="end"
             android:maxLines="2"
-            android:layout_below="@id/alert_icon"
+            android:layout_below="@id/icon"
             android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
             android:visibility="gone" />
     </com.android.settings.notification.NotificationButtonRelativeLayout>
@@ -120,7 +120,7 @@
         android:clickable="true"
         android:focusable="true">
         <ImageView
-            android:id="@+id/silence_icon"
+            android:id="@+id/icon"
             android:src="@drawable/ic_notifications_off_24dp"
             android:background="@android:color/transparent"
             android:layout_gravity="center"
@@ -129,19 +129,19 @@
             android:clickable="false"
             android:focusable="false"/>
         <TextView
-            android:id="@+id/silence_label"
+            android:id="@+id/label"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
             android:clickable="false"
             android:focusable="false"
-            android:layout_toEndOf="@id/silence_icon"
+            android:layout_toEndOf="@id/icon"
             android:layout_marginStart="@dimen/notification_importance_drawable_padding"
             android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
             android:text="@string/notification_silence_title"/>
         <TextView
-            android:id="@+id/silence_summary"
+            android:id="@+id/summary"
             android:paddingTop="@dimen/notification_importance_button_padding"
             android:text="@string/notification_conversation_summary_low"
             android:layout_width="match_parent"
@@ -150,7 +150,7 @@
             android:focusable="false"
             android:ellipsize="end"
             android:maxLines="2"
-            android:layout_below="@id/silence_icon"
+            android:layout_below="@id/icon"
             android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"
             android:visibility="gone" />
     </com.android.settings.notification.NotificationButtonRelativeLayout>
diff --git a/res/layout/notification_history_app_layout.xml b/res/layout/notification_history_app_layout.xml
index a1b3be7..aaca0fd 100644
--- a/res/layout/notification_history_app_layout.xml
+++ b/res/layout/notification_history_app_layout.xml
@@ -24,6 +24,7 @@
         android:id="@+id/app_header"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
+        android:background="@drawable/button_ripple_radius"
         android:paddingTop="12dp"
         android:paddingBottom="12dp"
         android:paddingStart="16dp">
@@ -54,17 +55,19 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:paddingStart="6dp"
-                android:layout_gravity="center_vertical"
+                android:layout_gravity="start|center_vertical"
+                android:textDirection="locale"
                 android:paddingTop="8dp"
                 android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"/>
         </LinearLayout>
-        <ImageButton
+        <ImageView
             android:id="@+id/expand"
             android:layout_alignParentEnd="true"
             android:layout_centerVertical="true"
             android:layout_height="48dp"
             android:layout_width="48dp"
-            android:background="@drawable/button_ripple_radius"
+            android:scaleType="center"
+            android:contentDescription="@null"
             android:src="@*android:drawable/ic_expand_more"/>
     </RelativeLayout>
 
diff --git a/res/layout/notification_sbn_log_row.xml b/res/layout/notification_sbn_log_row.xml
index 6156d26..ee143b0 100644
--- a/res/layout/notification_sbn_log_row.xml
+++ b/res/layout/notification_sbn_log_row.xml
@@ -130,6 +130,6 @@
     <View
         android:id="@+id/divider"
         android:layout_width="match_parent"
-        android:layout_height="0.5dp"
-        android:background="@color/material_grey_300" />
+        android:layout_height="wrap_content"
+        android:background="?android:attr/listDivider" />
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/panel_layout.xml b/res/layout/panel_layout.xml
index 895d09b..5f33c32 100644
--- a/res/layout/panel_layout.xml
+++ b/res/layout/panel_layout.xml
@@ -36,6 +36,7 @@
             android:visibility="gone">
 
             <LinearLayout
+                android:id="@+id/title_group"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
diff --git a/res/layout/settings_base_layout.xml b/res/layout/settings_base_layout.xml
index 0a4437e..9fbc2a2 100644
--- a/res/layout/settings_base_layout.xml
+++ b/res/layout/settings_base_layout.xml
@@ -26,8 +26,7 @@
         style="?android:attr/actionBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:theme="?android:attr/actionBarTheme"
-        android:navigationContentDescription="@*android:string/back_button_label" />
+        android:theme="?android:attr/actionBarTheme" />
     <FrameLayout
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index 45ecd61..1914462 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -141,6 +141,24 @@
                             android:prompt="@string/wifi_eap_method" />
                 </LinearLayout>
 
+                <LinearLayout android:id="@+id/l_sim"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:visibility="gone"
+                        style="@style/wifi_item" >
+                    <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            style="@style/wifi_item_label"
+                            android:text="@string/sim_card" />
+
+                    <Spinner android:id="@+id/sim"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            style="@style/wifi_item_spinner"
+                            android:prompt="@string/sim_card" />
+                </LinearLayout>
+
                 <LinearLayout android:id="@+id/l_phase2"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
@@ -196,18 +214,6 @@
                             android:entries="@array/eap_ocsp_type" />
                 </LinearLayout>
 
-                <LinearLayout android:id="@+id/no_ca_cert_warning"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:visibility="gone"
-                        style="@style/wifi_item" >
-                    <TextView
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            style="@style/wifi_item_warning"
-                            android:text="@string/wifi_do_not_validate_eap_server_warning" />
-                </LinearLayout>
-
                 <LinearLayout android:id="@+id/l_domain"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
diff --git a/res/raw/bubble_notification_animation.mp4 b/res/raw/bubble_notification_animation.mp4
index 2994548..563e18d 100644
--- a/res/raw/bubble_notification_animation.mp4
+++ b/res/raw/bubble_notification_animation.mp4
Binary files differ
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 4ce02ee..354da77 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -27,6 +27,7 @@
     <color name="notification_importance_selection_bg">@*android:color/material_grey_800</color>
     <color name="notification_importance_button_selected">#AECBFA</color> <!-- material blue 200 -->
     <color name="notification_importance_button_unselected">#5F6368</color>
+    <color name="notification_history_background">#202124</color>
     <color name="face_intro_outline">?android:attr/colorAccent</color>
     <!-- Palette list preference colors. -->
     <color name="palette_list_gradient_background">@android:color/black</color>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 06f135f..4904160 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -129,6 +129,7 @@
     <color name="notification_importance_button_unselected">#DADCE0</color>
     <color name="notification_importance_button_selected">#1967D2</color> <!-- material blue 700 -->
     <color name="notification_importance_selection_bg">#FFFFFF</color>
+    <color name="notification_history_background">?android:attr/colorBackgroundFloating</color>
 
     <!-- launcher icon color -->
     <color name="icon_launcher_setting_color">@*android:color/accent_device_default_light</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index fa4bc93..de0dcfb 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -459,4 +459,7 @@
 
     <!-- Whether to show Smooth Display feature in Settings Options -->
     <bool name="config_show_smooth_display">false</bool>
+
+    <!-- Whether to show the Preference for Adaptive connectivity -->
+    <bool name="config_show_adaptive_connectivity">false</bool>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8c6f9ba..7294d36 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -730,6 +730,12 @@
     <string name="security_settings_face_enroll_education_message"></string>
     <!-- Button that takes the user to the enrollment activity [CHAR LIMIT=20] -->
     <string name="security_settings_face_enroll_education_start">Start</string>
+    <!-- Confirmation dialog message shown when users with system accessibility features enabled try to start the non-accessibility version of enrollment [CHAR LIMIT=150] -->
+    <string name="security_settings_face_enroll_education_accessibility_dialog_message">If accessibility face unlock is turned off, some setup steps may not work properly with TalkBack.</string>
+    <!-- Negative button text for users who were shown the accessibility dialog [CHAR LIMIT=10] -->
+    <string name="security_settings_face_enroll_education_accessibility_dialog_negative">Go back</string>
+    <!-- Positive button text for users who were shown the accessibility dialog [CHAR LIMIT=20] -->
+    <string name="security_settings_face_enroll_education_accessibility_dialog_positive">Continue setup</string>
     <!-- Button shown which shows accessibility toggles for face enrollment when clicked. [CHAR LIMIT=32] -->
     <string name="security_settings_face_enroll_introduction_accessibility">Use accessibility setup</string>
     <!-- Additional details shown when the accessibility toggle is expanded. [CHAR LIMIT=NONE]-->
@@ -2112,9 +2118,7 @@
          when signal of the Wi-Fi network is reachable [CHAR LIMIT=50]  -->
     <string name="wifi_auto_connect_title">Auto\u2011connect</string>
     <!-- Hint for Wi-Fi Auto-connect [CHAR LIMIT=NONE]  -->
-    <string name="wifi_auto_connect_summary" product="default">Allow phone to automatically connect near this network</string>
-    <!-- Hint for Wi-Fi Auto-connect [CHAR LIMIT=NONE]  -->
-    <string name="wifi_auto_connect_summary" product="tablet">Allow tablet to automatically connect near this network</string>
+    <string name="wifi_auto_connect_summary">Allow connection to this network when in range</string>
     <!-- Label for "Use a QR code to add a device to this network" [CHAR LIMIT=50]  -->
     <string name="wifi_dpp_add_device">Add device</string>
     <!-- Hint for "Add device" [CHAR LIMIT=NONE]  -->
@@ -2137,8 +2141,6 @@
     <string name="wifi_do_not_provide_eap_user_cert">Do not provide</string>
     <!-- Menu option for not validating the EAP server -->
     <string name="wifi_do_not_validate_eap_server">Do not validate</string>
-    <!-- Warning message displayed if user choses not to validate the EAP server -->
-    <string name="wifi_do_not_validate_eap_server_warning">No certificate specified. Your connection will not be private.</string>
     <!-- Warning message displayed if network name (ssid) is too long -->
     <string name="wifi_ssid_too_long">Network name is too long.</string>
     <!-- Warning message displayed if user does not specify a domain for the CA certificate.
@@ -3868,7 +3870,7 @@
     <!-- Label for ethernet tether checkbox [CHAR LIMIT=NONE]-->
     <string name="ethernet_tether_checkbox_text">Ethernet tethering</string>
     <!-- Ethernet Tethering subtext [CHAR LIMIT=NONE]-->
-    <string name="ethernet_tethering_subtext" product="default">Share phone\u2019s internet connection via USB Ethernet</string>
+    <string name="ethernet_tethering_subtext" product="default">Share phone\u2019s internet connection via Ethernet</string>
 
     <!-- Tethering footer info [CHAR LIMIT=NONE]-->
     <string name="tethering_footer_info">Use hotspot and tethering to provide internet to other devices through your mobile data connection. Apps can also create a hotspot to share content with nearby devices.</string>
@@ -6190,6 +6192,11 @@
     <!-- Title of preference to enter the VPN settings activity -->
     <string name="vpn_settings_title">VPN</string>
 
+    <!-- Title of Adaptive connectivity. Adaptive connectivity is a feature which automatically manages network connections for better battery life and performance. [CHAR LIMIT=60] -->
+    <string name="adaptive_connectivity_title">Adaptive connectivity</string>
+    <!-- Summary of Adaptive connectivity preference. [CHAR LIMIT=NONE] -->
+    <string name="adaptive_connectivity_summary">Extends battery life and improves device performance by automatically managing your network connections</string>
+
     <!-- Title of preference group for credential storage settings [CHAR LIMIT=30] -->
     <string name="credentials_title">Credential storage</string>
     <!-- Title of preference to install certificates [CHAR LIMIT=30] -->
@@ -11487,6 +11494,17 @@
     <!-- Message informs the user that has no SIM card in personalized Settings [CHAR LIMIT=30] -->
     <string name="no_sim_card">No SIM card</string>
 
+    <!-- A title for a SIM card spinner UI component which users can choose a SIM card. [CHAR LIMIT=NONE] -->
+    <string name="sim_card">SIM</string>
+    <!-- A label when there is no SIM card to choose. [CHAR LIMIT=NONE] -->
+    <string name="wifi_no_sim_card">No SIM</string>
+    <!-- A label when there is no SIM card information of a Wi-Fi network. [CHAR LIMIT=NONE] -->
+    <string name="wifi_no_related_sim_card">None</string>
+    <!-- A label wen a user needs a SIM to connect to the Wi-Fi network. [CHAR LIMIT=NONE] -->
+    <string name="wifi_require_sim_card_to_connect">Requires SIM to connect</string>
+    <!-- A label wen a user needs a specific SIM to connect to the Wi-Fi network. [CHAR LIMIT=NONE] -->
+    <string name="wifi_require_specific_sim_card_to_connect">Requires <xliff:g id="wireless_carrier" example="Verizon">%s</xliff:g> SIM to connect</string>
+
     <!-- The following strings are summaries for preferred network modes in Mobile network settings,
      and have a character limit of 100 -->
     <!-- WCDMA preferred [CHAR LIMIT=NONE] -->
@@ -12144,10 +12162,10 @@
 
     <!-- Title for media control settings [CHAR LIMIT=50]-->
     <string name="media_controls_title">Media</string>
-    <!-- Summary for media control settings [CHAR LIMIT=60]-->
-    <string name="media_controls_summary">Hide player when the media session has ended</string>
+    <!-- Summary for media control settings [CHAR LIMIT=NONE]-->
+    <string name="media_controls_summary">Media player in Quick Settings</string>
     <!-- Description of toggle to enable or disable the media resumption feature in quick settings [CHAR LIMIT=NONE]-->
-    <string name="media_controls_resume_description">The player allows you to resume a session from the expanded Quick Settings panel.</string>
+    <string name="media_controls_resume_description">Show media player for an extended period to easily resume playback</string>
     <!-- Subtext for media settings when the player will be hidden [CHAR LIMIT=50] -->
     <string name="media_controls_hide_player">Hide player</string>
     <!-- Subtext for media settings when the player will be shown [CHAR LIMIT=50] -->
@@ -12156,4 +12174,7 @@
     <string name="media_controls_no_players">No players available</string>
     <!-- Keywords for the media controls setting [CHAR LIMIT=NONE]-->
     <string name="keywords_media_controls">media</string>
+
+    <!-- Summary for see all preference when bluetooth is disable [CHAR LIMIT=none]-->
+    <string name="connected_device_see_all_summary">Bluetooth will turn on</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c74a1a2..1be8ba0 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -618,7 +618,7 @@
 
     <style name="TextAppearance.NotificationImportanceButton">
         <item name="android:textSize">@dimen/notification_importance_button_text</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:gravity">center</item>
     </style>
 
diff --git a/res/xml/adaptive_connectivity_settings.xml b/res/xml/adaptive_connectivity_settings.xml
new file mode 100644
index 0000000..ff9bdb0
--- /dev/null
+++ b/res/xml/adaptive_connectivity_settings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:title="@string/adaptive_connectivity_title">
+
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="adaptive_connectivity_header"
+        android:layout="@layout/adaptive_connectivity_header"
+        android:persistent="false"
+        android:selectable="false"
+        android:title="@string/summary_placeholder"
+        settings:allowDividerBelow="true"
+        settings:searchable="false"/>
+
+    <SwitchPreference
+        android:key="adaptive_connectivity"
+        android:title="@string/adaptive_connectivity_title"
+        android:summary="@string/adaptive_connectivity_summary"
+        settings:allowDividerAbove="true"
+        settings:controller="com.android.settings.network.AdaptiveConnectivityTogglePreferenceController"/>
+
+</PreferenceScreen>
diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml
index 966034e..29b82ef 100644
--- a/res/xml/battery_saver_settings.xml
+++ b/res/xml/battery_saver_settings.xml
@@ -34,7 +34,7 @@
         settings:controller="com.android.settings.fuelgauge.batterysaver.BatterySaverStickyPreferenceController"/>
 
     <com.android.settings.widget.TwoStateButtonPreference
-        android:key="battery_saver_button"
+        android:key="battery_saver"
         android:title="@string/battery_saver"
         android:selectable="false"
         android:summary="@string/battery_saver_turn_on_summary"
diff --git a/res/xml/network_and_internet.xml b/res/xml/network_and_internet.xml
index eaa7808..6bf6dba 100644
--- a/res/xml/network_and_internet.xml
+++ b/res/xml/network_and_internet.xml
@@ -118,4 +118,11 @@
         android:positiveButtonText="@string/save"
         android:negativeButtonText="@android:string/cancel" />
 
+    <Preference
+        android:fragment="com.android.settings.network.AdaptiveConnectivitySettings"
+        android:key="adaptive_connectivity"
+        android:title="@string/adaptive_connectivity_title"
+        android:summary="@string/summary_placeholder"
+        android:order="25"
+        settings:controller="com.android.settings.network.AdaptiveConnectivityPreferenceController"/>
 </PreferenceScreen>
diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml
index 92f68cc..181b799 100644
--- a/res/xml/wifi_network_details_fragment2.xml
+++ b/res/xml/wifi_network_details_fragment2.xml
@@ -106,6 +106,11 @@
                 android:selectable="false"
                 settings:enableCopying="true"/>
         <Preference
+                android:key="eap_sim_subscription"
+                android:title="@string/sim_card"
+                android:selectable="false"
+                settings:enableCopying="true"/>
+        <Preference
                 android:key="mac_address"
                 android:title="@string/wifi_advanced_randomized_mac_address_title"
                 android:selectable="false"
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 0325842..26c699b 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -80,6 +80,10 @@
     @VisibleForTesting
     static final String KEY_TETHER_PREFS_FOOTER = "tether_prefs_footer";
 
+    @VisibleForTesting
+    static final String BLUETOOTH_TETHERING_STATE_CHANGED =
+            "android.bluetooth.pan.profile.action.TETHERING_STATE_CHANGED";
+
     private static final String TAG = "TetheringSettings";
 
     private SwitchPreference mUsbTether;
@@ -154,9 +158,7 @@
                     BluetoothProfile.PAN);
         }
 
-        mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
-        mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
-        mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING);
+        setupTetherPreference();
         setFooterPreferenceTitle();
 
         mDataSaverBackend.addListener(this);
@@ -208,6 +210,13 @@
         super.onDestroy();
     }
 
+    @VisibleForTesting
+    void setupTetherPreference() {
+        mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
+        mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
+        mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING);
+    }
+
     @Override
     public void onDataSaverChanged(boolean isDataSaving) {
         mDataSaverEnabled = isDataSaving;
@@ -281,6 +290,8 @@
                     }
                 }
                 updateState();
+            } else if (action.equals(BLUETOOTH_TETHERING_STATE_CHANGED)) {
+                updateState();
             }
         }
     }
@@ -297,32 +308,13 @@
             return;
         }
 
-        final Activity activity = getActivity();
 
         mStartTetheringCallback = new OnStartTetheringCallback(this);
         mTetheringEventCallback = new TetheringEventCallback();
         mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback);
 
         mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
-        mTetherChangeReceiver = new TetherChangeReceiver();
-        IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
-        Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
-
-        filter = new IntentFilter();
-        filter.addAction(UsbManager.ACTION_USB_STATE);
-        activity.registerReceiver(mTetherChangeReceiver, filter);
-
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_MEDIA_SHARED);
-        filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
-        filter.addDataScheme("file");
-        activity.registerReceiver(mTetherChangeReceiver, filter);
-
-        filter = new IntentFilter();
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        activity.registerReceiver(mTetherChangeReceiver, filter);
-
-        if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
+        registerReceiver();
 
         mEthernetListener = new EthernetListener();
         if (mEm != null)
@@ -348,10 +340,38 @@
         mEthernetListener = null;
     }
 
+    @VisibleForTesting
+    void registerReceiver() {
+        final Activity activity = getActivity();
+
+        mTetherChangeReceiver = new TetherChangeReceiver();
+        IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        final Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
+
+        filter = new IntentFilter();
+        filter.addAction(UsbManager.ACTION_USB_STATE);
+        activity.registerReceiver(mTetherChangeReceiver, filter);
+
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_MEDIA_SHARED);
+        filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
+        filter.addDataScheme("file");
+        activity.registerReceiver(mTetherChangeReceiver, filter);
+
+        filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(BLUETOOTH_TETHERING_STATE_CHANGED);
+        activity.registerReceiver(mTetherChangeReceiver, filter);
+
+        if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
+    }
+
     private void updateState() {
-        String[] available = mCm.getTetherableIfaces();
-        String[] tethered = mCm.getTetheredIfaces();
-        String[] errored = mCm.getTetheringErroredIfaces();
+        final ConnectivityManager cm =
+                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+        final String[] available = cm.getTetherableIfaces();
+        final String[] tethered = cm.getTetheredIfaces();
+        final String[] errored = cm.getTetheringErroredIfaces();
         updateState(available, tethered, errored);
     }
 
@@ -362,7 +382,8 @@
         updateEthernetState(available, tethered);
     }
 
-    private void updateUsbState(String[] available, String[] tethered,
+    @VisibleForTesting
+    void updateUsbState(String[] available, String[] tethered,
             String[] errored) {
         boolean usbAvailable = mUsbConnected && !mMassStorageActive;
         int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
@@ -400,20 +421,33 @@
         }
     }
 
-    private void updateBluetoothState() {
+    @VisibleForTesting
+    int getBluetoothState() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter == null) {
+            return BluetoothAdapter.ERROR;
+        }
+        return adapter.getState();
+    }
+
+    @VisibleForTesting
+    boolean isBluetoothTetheringOn() {
+        final BluetoothPan bluetoothPan = mBluetoothPan.get();
+        return bluetoothPan != null && bluetoothPan.isTetheringOn();
+    }
+
+    private void updateBluetoothState() {
+        final int btState = getBluetoothState();
+        if (btState == BluetoothAdapter.ERROR) {
             return;
         }
-        int btState = adapter.getState();
+
         if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
             mBluetoothTether.setEnabled(false);
         } else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
             mBluetoothTether.setEnabled(false);
         } else {
-            BluetoothPan bluetoothPan = mBluetoothPan.get();
-            if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null
-                    && bluetoothPan.isTetheringOn()) {
+            if (btState == BluetoothAdapter.STATE_ON && isBluetoothTetheringOn()) {
                 mBluetoothTether.setChecked(true);
                 mBluetoothTether.setEnabled(!mDataSaverEnabled);
             } else {
@@ -423,7 +457,8 @@
         }
     }
 
-    private void updateEthernetState(String[] available, String[] tethered) {
+    @VisibleForTesting
+    void updateEthernetState(String[] available, String[] tethered) {
 
         boolean isAvailable = false;
         boolean isTethered = false;
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 64919d9..11a5825 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -18,6 +18,10 @@
 
 import static android.content.Intent.EXTRA_USER;
 import static android.content.Intent.EXTRA_USER_ID;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
@@ -53,6 +57,8 @@
 import android.graphics.drawable.VectorDrawable;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -1137,4 +1143,31 @@
         drawable.draw(canvas);
         return roundedBitmap;
     }
+
+    /**
+     * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
+     */
+    public static boolean isMediaOutputDisabled(
+            MediaRouter2Manager router2Manager, String packageName) {
+        boolean isMediaOutputDisabled = false;
+        if (!TextUtils.isEmpty(packageName)) {
+            final List<MediaRoute2Info> infos = router2Manager.getAvailableRoutes(packageName);
+            if (infos.size() == 1) {
+                final MediaRoute2Info info = infos.get(0);
+                final int deviceType = info.getType();
+                switch (deviceType) {
+                    case TYPE_UNKNOWN:
+                    case TYPE_REMOTE_TV:
+                    case TYPE_REMOTE_SPEAKER:
+                    case TYPE_GROUP:
+                        isMediaOutputDisabled = true;
+                        break;
+                    default:
+                        isMediaOutputDisabled = false;
+                        break;
+                }
+            }
+        }
+        return isMediaOutputDisabled;
+    }
 }
diff --git a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
index 58907a7..28e0850 100644
--- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
+++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
@@ -58,12 +58,12 @@
     // Filters will appear sorted based on their value defined here.
     public static final int FILTER_APPS_POWER_WHITELIST = 0;
     public static final int FILTER_APPS_POWER_WHITELIST_ALL = 1;
-    public static final int FILTER_APPS_ALL = 2;
-    public static final int FILTER_APPS_ENABLED = 3;
-    public static final int FILTER_APPS_INSTANT = 4;
-    public static final int FILTER_APPS_DISABLED = 5;
-    public static final int FILTER_APPS_RECENT = 6;
-    public static final int FILTER_APPS_FREQUENT = 7;
+    public static final int FILTER_APPS_RECENT = 2;
+    public static final int FILTER_APPS_FREQUENT = 3;
+    public static final int FILTER_APPS_ALL = 4;
+    public static final int FILTER_APPS_ENABLED = 5;
+    public static final int FILTER_APPS_INSTANT = 6;
+    public static final int FILTER_APPS_DISABLED = 7;
     public static final int FILTER_APPS_PERSONAL = 8;
     public static final int FILTER_APPS_WORK = 9;
     public static final int FILTER_APPS_USAGE_ACCESS = 10;
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 5ef1232..b46e11c 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -476,7 +476,7 @@
             mFilterAdapter.enableFilter(FILTER_APPS_RECENT);
             mFilterAdapter.enableFilter(FILTER_APPS_FREQUENT);
             mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
-            mFilterAdapter.disableFilter(FILTER_APPS_ALL);
+            mFilterAdapter.enableFilter(FILTER_APPS_ALL);
         }
         if (mListType == LIST_TYPE_HIGH_POWER) {
             mFilterAdapter.enableFilter(FILTER_APPS_POWER_WHITELIST_ALL);
@@ -1089,12 +1089,16 @@
             mAppFilter = appFilter;
 
             // Notification filters require resorting the list
-            if (FILTER_APPS_FREQUENT == appFilter.getFilterType()) {
-                rebuild(R.id.sort_order_frequent_notification);
-            } else if (FILTER_APPS_RECENT == appFilter.getFilterType()) {
-                rebuild(R.id.sort_order_recent_notification);
-            } else if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
-                rebuild(R.id.sort_order_alpha);
+            if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
+                if (FILTER_APPS_FREQUENT == appFilter.getFilterType()) {
+                    rebuild(R.id.sort_order_frequent_notification);
+                } else if (FILTER_APPS_RECENT == appFilter.getFilterType()) {
+                    rebuild(R.id.sort_order_recent_notification);
+                } else if (FILTER_APPS_BLOCKED == appFilter.getFilterType()) {
+                    rebuild(R.id.sort_order_alpha);
+                } else {
+                    rebuild(R.id.sort_order_alpha);
+                }
             } else {
                 rebuild();
             }
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
index 727870c..e6b389a 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
@@ -40,20 +40,13 @@
 
     private static final String TAG = "ZenAccessController";
 
-    private final ActivityManager mActivityManager;
-
     public ZenAccessController(Context context, String preferenceKey) {
         super(context, preferenceKey);
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     @Override
     public int getAvailabilityStatus() {
-        return isSupported(mActivityManager) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
-    }
-
-    public static boolean isSupported(ActivityManager activityManager) {
-        return !activityManager.isLowRamDevice();
+        return AVAILABLE;
     }
 
     public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
index a18e7d6..ba6bb1d 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -50,9 +50,6 @@
     @Override
     protected boolean refreshUi() {
         final Context context = getContext();
-        if (!ZenAccessController.isSupported(context.getSystemService(ActivityManager.class))) {
-            return false;
-        }
         // If this app didn't declare this permission in their manifest, don't bother showing UI.
         final Set<String> needAccessApps =
                 ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
index 30507ef..da238f6 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
@@ -53,9 +53,6 @@
 
     @Override
     public void onStart() {
-        if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
-            return;
-        }
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(
                         Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES),
@@ -69,9 +66,6 @@
 
     @Override
     public void onStop() {
-        if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
-            return;
-        }
         mContext.getContentResolver().unregisterContentObserver(this /* observer */);
     }
 }
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityDialog.java b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityDialog.java
new file mode 100644
index 0000000..7042870
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollAccessibilityDialog.java
@@ -0,0 +1,71 @@
+/*
+ * 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.biometrics.face;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Confirmation dialog shown to users with accessibility enabled who are trying to start the
+ * non-accessibility enrollment flow.
+ */
+public class FaceEnrollAccessibilityDialog extends InstrumentedDialogFragment {
+    private AlertDialog.OnClickListener mPositiveButtonListener;
+
+    /**
+     * @return new instance of the dialog
+     */
+    public static FaceEnrollAccessibilityDialog newInstance() {
+        return new FaceEnrollAccessibilityDialog();
+    }
+
+    public void setPositiveButtonListener(AlertDialog.OnClickListener listener) {
+        mPositiveButtonListener = listener;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+        final int titleResId =
+                R.string.security_settings_face_enroll_education_accessibility_dialog_message;
+        final int negativeButtonResId =
+                R.string.security_settings_face_enroll_education_accessibility_dialog_negative;
+        final int positiveButtonResId =
+                R.string.security_settings_face_enroll_education_accessibility_dialog_positive;
+
+        builder.setMessage(titleResId)
+                .setNegativeButton(negativeButtonResId, (dialog, which) -> {
+                    dialog.cancel();
+                })
+                .setPositiveButton(positiveButtonResId, (dialog, which) -> {
+                    mPositiveButtonListener.onClick(dialog, which);
+                });
+
+        return builder.create();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.FACE_ENROLL_INTRO;
+    }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
index 3b84c35..4c4fa11 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
@@ -55,6 +55,7 @@
     private Intent mResultIntent;
     private TextView mDescriptionText;
     private boolean mNextClicked;
+    private boolean mAccessibilityEnabled;
 
     private CompoundButton.OnCheckedChangeListener mSwitchDiversityListener =
             new CompoundButton.OnCheckedChangeListener() {
@@ -123,13 +124,12 @@
                 .setTheme(R.style.SudGlifButton_Primary)
                 .build();
 
-        boolean accessibilityEnabled = false;
         final AccessibilityManager accessibilityManager = getApplicationContext().getSystemService(
                 AccessibilityManager.class);
         if (accessibilityManager != null) {
             // Add additional check for touch exploration. This prevents other accessibility
             // features such as Live Transcribe from defaulting to the accessibility setup.
-            accessibilityEnabled = accessibilityManager.isEnabled()
+            mAccessibilityEnabled = accessibilityManager.isEnabled()
                     && accessibilityManager.isTouchExplorationEnabled();
         }
         mFooterBarMixin.setPrimaryButton(footerButton);
@@ -147,7 +147,7 @@
             mSwitchDiversity.getSwitch().toggle();
         });
 
-        if (accessibilityEnabled) {
+        if (mAccessibilityEnabled) {
             accessibilityButton.callOnClick();
         }
     }
@@ -194,9 +194,20 @@
         if (mResultIntent != null) {
             intent.putExtras(mResultIntent);
         }
-        mNextClicked = true;
+
         intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
-        startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
+
+        if (!mSwitchDiversity.isChecked() && mAccessibilityEnabled) {
+            FaceEnrollAccessibilityDialog dialog = FaceEnrollAccessibilityDialog.newInstance();
+            dialog.setPositiveButtonListener((dialog1, which) -> {
+                startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
+                mNextClicked = true;
+            });
+            dialog.show(getSupportFragmentManager(), FaceEnrollAccessibilityDialog.class.getName());
+        } else {
+            startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
+            mNextClicked = true;
+        }
     }
 
     protected void onSkipButtonClick(View view) {
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 86b72ec..d927121 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -52,8 +52,7 @@
  * BluetoothDevicePreference is the preference type used to display each remote
  * Bluetooth device in the Bluetooth Settings screen.
  */
-public final class BluetoothDevicePreference extends GearPreference implements
-        CachedBluetoothDevice.Callback {
+public final class BluetoothDevicePreference extends GearPreference {
     private static final String TAG = "BluetoothDevicePref";
 
     private static int sDimAlpha = Integer.MIN_VALUE;
@@ -77,10 +76,20 @@
     private AlertDialog mDisconnectDialog;
     private String contentDescription = null;
     private boolean mHideSecondTarget = false;
+    private boolean mIsCallbackRemoved = false;
     @VisibleForTesting
     boolean mNeedNotifyHierarchyChanged = false;
     /* Talk-back descriptions for various BT icons */
     Resources mResources;
+    final BluetoothDevicePreferenceCallback mCallback;
+
+    private class BluetoothDevicePreferenceCallback implements CachedBluetoothDevice.Callback {
+
+        @Override
+        public void onDeviceAttributesChanged() {
+            onPreferenceAttributesChanged();
+        }
+    }
 
     public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
             boolean showDeviceWithoutNames, @SortType int type) {
@@ -96,11 +105,12 @@
         }
 
         mCachedDevice = cachedDevice;
-        mCachedDevice.registerCallback(this);
+        mCallback = new BluetoothDevicePreferenceCallback();
+        mCachedDevice.registerCallback(mCallback);
         mCurrentTime = System.currentTimeMillis();
         mType = type;
 
-        onDeviceAttributesChanged();
+        onPreferenceAttributesChanged();
     }
 
     public void setNeedNotifyHierarchyChanged(boolean needNotifyHierarchyChanged) {
@@ -127,13 +137,35 @@
     @Override
     protected void onPrepareForRemoval() {
         super.onPrepareForRemoval();
-        mCachedDevice.unregisterCallback(this);
+        if (!mIsCallbackRemoved) {
+            mCachedDevice.unregisterCallback(mCallback);
+            mIsCallbackRemoved = true;
+        }
         if (mDisconnectDialog != null) {
             mDisconnectDialog.dismiss();
             mDisconnectDialog = null;
         }
     }
 
+    @Override
+    public void onAttached() {
+        super.onAttached();
+        if (mIsCallbackRemoved) {
+            mCachedDevice.registerCallback(mCallback);
+            mIsCallbackRemoved = false;
+        }
+        onPreferenceAttributesChanged();
+    }
+
+    @Override
+    public void onDetached() {
+        super.onDetached();
+        if (!mIsCallbackRemoved) {
+            mCachedDevice.unregisterCallback(mCallback);
+            mIsCallbackRemoved = true;
+        }
+    }
+
     public CachedBluetoothDevice getBluetoothDevice() {
         return mCachedDevice;
     }
@@ -142,7 +174,7 @@
         mHideSecondTarget = hideSecondTarget;
     }
 
-    public void onDeviceAttributesChanged() {
+    private void onPreferenceAttributesChanged() {
         /*
          * The preference framework takes care of making sure the value has
          * changed before proceeding. It will also call notifyChanged() if
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index 02625bb..ab8eea5 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -18,6 +18,7 @@
 
 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
 
+import android.Manifest;
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -192,6 +193,6 @@
         if (mLaunchPackage != null && mLaunchClass != null) {
             intent.setClassName(mLaunchPackage, mLaunchClass);
         }
-        getActivity().sendBroadcast(intent);
+        getActivity().sendBroadcast(intent, Manifest.permission.BLUETOOTH_ADMIN);
     }
 }
diff --git a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
index 466d60e..dab4f23 100644
--- a/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
@@ -24,6 +24,7 @@
 import androidx.preference.Preference;
 
 import com.android.settings.connecteddevice.DevicePreferenceCallback;
+import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -42,13 +43,15 @@
 
     private static final String PREF_KEY = "saved_bt";
 
+    private final boolean mDisplayConnected;
+
     @VisibleForTesting
     BluetoothAdapter mBluetoothAdapter;
 
     public SavedBluetoothDeviceUpdater(Context context, DashboardFragment fragment,
             DevicePreferenceCallback devicePreferenceCallback) {
         super(context, fragment, devicePreferenceCallback);
-
+        mDisplayConnected = (fragment instanceof PreviouslyConnectedDeviceDashboardFragment);
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
@@ -101,7 +104,8 @@
                     ", is connected : " + device.isConnected() + ", is profile connected : "
                     + cachedDevice.isConnected());
         }
-        return device.getBondState() == BluetoothDevice.BOND_BONDED && !device.isConnected();
+        return device.getBondState() == BluetoothDevice.BOND_BONDED
+                && (mDisplayConnected || !device.isConnected());
     }
 
     @Override
@@ -109,6 +113,9 @@
         mMetricsFeatureProvider.logClickedPreference(preference, mFragment.getMetricsCategory());
         final CachedBluetoothDevice device = ((BluetoothDevicePreference) preference)
                 .getBluetoothDevice();
+        if (device.isConnected()) {
+            return device.setActive();
+        }
         device.connect();
         return true;
     }
diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragment.java
index d3f5c28..05a17cc 100644
--- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragment.java
@@ -16,7 +16,11 @@
 package com.android.settings.connecteddevice;
 
 import android.app.settings.SettingsEnums;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
 
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
@@ -32,6 +36,9 @@
     private static final String TAG = "PreConnectedDeviceFrag";
     static final String KEY_PREVIOUSLY_CONNECTED_DEVICES = "saved_device_list";
 
+    @VisibleForTesting
+    BluetoothAdapter mBluetoothAdapter;
+
     @Override
     public int getHelpResource() {
         return R.string.help_url_previously_connected_devices;
@@ -53,11 +60,31 @@
     }
 
     @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    @Override
     public void onAttach(Context context) {
         super.onAttach(context);
         use(SavedDeviceGroupController.class).init(this);
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        enableBluetoothIfNecessary();
+    }
+
+    @VisibleForTesting
+    void enableBluetoothIfNecessary() {
+        if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
+            mBluetoothAdapter.enable();
+        }
+    }
+
     /**
      * For Search.
      */
diff --git a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
index 5b23d69..438a381 100644
--- a/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
+++ b/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceController.java
@@ -15,14 +15,22 @@
  */
 package com.android.settings.connecteddevice;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDevicePreference;
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
 import com.android.settings.connecteddevice.dock.DockUpdater;
@@ -33,21 +41,47 @@
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController
         implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback {
 
+    private static final String TAG = "PreviouslyDevicePreController";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
     private static final int MAX_DEVICE_NUM = 3;
+    private static final int DOCK_DEVICE_INDEX = 9;
+    private static final String KEY_SEE_ALL = "previously_connected_devices_see_all";
+
+    private final List<Preference> mDevicesList = new ArrayList<>();
+    private final List<Preference> mDockDevicesList = new ArrayList<>();
 
     private PreferenceGroup mPreferenceGroup;
     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
     private DockUpdater mSavedDockUpdater;
-    private int mPreferenceSize;
+    private BluetoothAdapter mBluetoothAdapter;
+
+    @VisibleForTesting
+    Preference mSeeAllPreference;
+    @VisibleForTesting
+    IntentFilter mIntentFilter;
+
+    @VisibleForTesting
+    BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updatePreferenceVisibility();
+        }
+    };
 
     public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
 
         mSavedDockUpdater = FeatureFactory.getFactory(
                 context).getDockUpdaterFeatureProvider().getSavedDockUpdater(context, this);
+        mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Override
@@ -62,7 +96,8 @@
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mPreferenceGroup = screen.findPreference(getPreferenceKey());
-        mPreferenceGroup.setVisible(false);
+        mSeeAllPreference = mPreferenceGroup.findPreference(KEY_SEE_ALL);
+        updatePreferenceVisibility();
 
         if (isAvailable()) {
             final Context context = screen.getContext();
@@ -75,12 +110,14 @@
     public void onStart() {
         mBluetoothDeviceUpdater.registerCallback();
         mSavedDockUpdater.registerCallback();
+        mContext.registerReceiver(mReceiver, mIntentFilter);
     }
 
     @Override
     public void onStop() {
         mBluetoothDeviceUpdater.unregisterCallback();
         mSavedDockUpdater.unregisterCallback();
+        mContext.unregisterReceiver(mReceiver);
     }
 
     public void init(DashboardFragment fragment) {
@@ -90,18 +127,77 @@
 
     @Override
     public void onDeviceAdded(Preference preference) {
-        mPreferenceSize++;
-        if (mPreferenceSize <= MAX_DEVICE_NUM) {
-            mPreferenceGroup.addPreference(preference);
+        final List<BluetoothDevice> bluetoothDevices =
+                mBluetoothAdapter.getMostRecentlyConnectedDevices();
+        final int index = preference instanceof BluetoothDevicePreference
+                ? bluetoothDevices.indexOf(((BluetoothDevicePreference) preference)
+                .getBluetoothDevice().getDevice()) : DOCK_DEVICE_INDEX;
+        if (DEBUG) {
+            Log.d(TAG, "onDeviceAdded() " + preference.getTitle() + ", index of : " + index);
+            for (BluetoothDevice device : bluetoothDevices) {
+                Log.d(TAG, "onDeviceAdded() most recently device : " + device.getName());
+            }
         }
-        updatePreferenceVisiblity();
+        addPreference(index, preference);
+        updatePreferenceVisibility();
+    }
+
+    private void addPreference(int index, Preference preference) {
+        if (preference instanceof BluetoothDevicePreference) {
+            if (mDevicesList.size() >= index) {
+                mDevicesList.add(index, preference);
+            } else {
+                mDevicesList.add(preference);
+            }
+        } else {
+            mDockDevicesList.add(preference);
+        }
+        addPreference();
+    }
+
+    private void addPreference() {
+        mPreferenceGroup.removeAll();
+        mPreferenceGroup.addPreference(mSeeAllPreference);
+        final int size = getDeviceListSize();
+        for (int i = 0; i < size; i++) {
+            if (DEBUG) {
+                Log.d(TAG, "addPreference() add device : " + mDevicesList.get(i).getTitle());
+            }
+            mDevicesList.get(i).setOrder(i);
+            mPreferenceGroup.addPreference(mDevicesList.get(i));
+        }
+        if (mDockDevicesList.size() > 0) {
+            for (int i = 0; i < getDockDeviceListSize(MAX_DEVICE_NUM - size); i++) {
+                if (DEBUG) {
+                    Log.d(TAG, "addPreference() add dock device : "
+                            + mDockDevicesList.get(i).getTitle());
+                }
+                mDockDevicesList.get(i).setOrder(DOCK_DEVICE_INDEX);
+                mPreferenceGroup.addPreference(mDockDevicesList.get(i));
+            }
+        }
+    }
+
+    private int getDeviceListSize() {
+        return mDevicesList.size() >= MAX_DEVICE_NUM
+                ? MAX_DEVICE_NUM : mDevicesList.size();
+    }
+
+    private int getDockDeviceListSize(int availableSize) {
+        return mDockDevicesList.size() >= availableSize
+                ? availableSize : mDockDevicesList.size();
     }
 
     @Override
     public void onDeviceRemoved(Preference preference) {
-        mPreferenceSize--;
-        mPreferenceGroup.removePreference(preference);
-        updatePreferenceVisiblity();
+        if (preference instanceof BluetoothDevicePreference) {
+            mDevicesList.remove(preference);
+        } else {
+            mDockDevicesList.remove(preference);
+        }
+
+        addPreference();
+        updatePreferenceVisibility();
     }
 
     @VisibleForTesting
@@ -120,7 +216,12 @@
     }
 
     @VisibleForTesting
-    void updatePreferenceVisiblity() {
-        mPreferenceGroup.setVisible(mPreferenceSize > 0);
+    void updatePreferenceVisibility() {
+        if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
+            mSeeAllPreference.setSummary("");
+        } else {
+            mSeeAllPreference.setSummary(
+                    mContext.getString(R.string.connected_device_see_all_summary));
+        }
     }
 }
diff --git a/src/com/android/settings/connecteddevice/usb/UsbBackend.java b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
index 556f76d..4773aca 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbBackend.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbBackend.java
@@ -50,6 +50,7 @@
     private final boolean mTetheringRestrictedBySystem;
     private final boolean mMidiSupported;
     private final boolean mTetheringSupported;
+    private final boolean mIsAdminUser;
 
     private UsbManager mUsbManager;
 
@@ -70,6 +71,7 @@
         mFileTransferRestrictedBySystem = isUsbFileTransferRestrictedBySystem(userManager);
         mTetheringRestricted = isUsbTetheringRestricted(userManager);
         mTetheringRestrictedBySystem = isUsbTetheringRestrictedBySystem(userManager);
+        mIsAdminUser = userManager.isAdminUser();
 
         mMidiSupported = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
         ConnectivityManager cm =
@@ -100,7 +102,8 @@
                 || (!mTetheringSupported && (functions & UsbManager.FUNCTION_RNDIS) != 0)) {
             return false;
         }
-        return !(areFunctionDisallowed(functions) || areFunctionsDisallowedBySystem(functions));
+        return !(areFunctionDisallowed(functions) || areFunctionsDisallowedBySystem(functions)
+                || areFunctionsDisallowedByNonAdminUser(functions));
     }
 
     public int getPowerRole() {
@@ -207,6 +210,11 @@
                 || (mTetheringRestrictedBySystem && ((functions & UsbManager.FUNCTION_RNDIS) != 0));
     }
 
+    @VisibleForTesting
+    boolean areFunctionsDisallowedByNonAdminUser(long functions) {
+        return !mIsAdminUser && (functions & UsbManager.FUNCTION_RNDIS) != 0;
+    }
+
     private void updatePorts() {
         mPort = null;
         mPortStatus = null;
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 7640079..e8ae85d 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -248,7 +248,9 @@
             final Map<String, IContentProvider> providerMap = new ArrayMap<>();
             final String titleFromUri = TileUtils.getTextFromUri(
                     mContext, uri, providerMap, META_DATA_PREFERENCE_TITLE);
-            ThreadUtils.postOnMainThread(() -> preference.setTitle(titleFromUri));
+            if (!TextUtils.equals(titleFromUri, preference.getTitle())) {
+                ThreadUtils.postOnMainThread(() -> preference.setTitle(titleFromUri));
+            }
         });
     }
 
@@ -277,7 +279,9 @@
             final Map<String, IContentProvider> providerMap = new ArrayMap<>();
             final String summaryFromUri = TileUtils.getTextFromUri(
                     mContext, uri, providerMap, META_DATA_PREFERENCE_SUMMARY);
-            ThreadUtils.postOnMainThread(() -> preference.setSummary(summaryFromUri));
+            if (!TextUtils.equals(summaryFromUri, preference.getSummary())) {
+                ThreadUtils.postOnMainThread(() -> preference.setSummary(summaryFromUri));
+            }
         });
     }
 
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index 97cf599..51f5213 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -325,7 +325,10 @@
                 mSnapshotTime = primaryPlan.getDataUsageTime();
             }
         }
-        mManageSubscriptionIntent = createManageSubscriptionIntent(mSubId);
+        // Temporarily return null, since no current users of SubscriptionPlan have this intent set.
+        // TODO (b/170330084): Remove after refactoring 5G SubscriptionPlan logic.
+        // mManageSubscriptionIntent = createManageSubscriptionIntent(mSubId);
+        mManageSubscriptionIntent = null;
         Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + mSubId
                 + ", intent " + mManageSubscriptionIntent);
     }
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index f2d60f1..8eb5c4f 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.development;
 
+import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
+
 import android.app.Activity;
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothA2dp;
@@ -23,12 +25,14 @@
 import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -41,6 +45,7 @@
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.RestrictedDashboardFragment;
 import com.android.settings.development.autofill.AutofillLoggingLevelPreferenceController;
 import com.android.settings.development.autofill.AutofillResetOptionsPreferenceController;
@@ -52,6 +57,7 @@
 import com.android.settings.development.bluetooth.BluetoothHDAudioPreferenceController;
 import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferenceController;
 import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
+import com.android.settings.development.qstile.DevelopmentTiles;
 import com.android.settings.development.storage.SharedDataPreferenceController;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.widget.SwitchBar;
@@ -199,11 +205,42 @@
         // Restore UI state based on whether developer options is enabled
         if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext())) {
             enableDeveloperOptions();
+            handleQsTileLongPressActionIfAny();
         } else {
             disableDeveloperOptions();
         }
     }
 
+    /**
+     * Long-pressing a developer options quick settings tile will by default (see
+     * QS_TILE_PREFERENCES in the manifest) take you to the developer options page.
+     * Some tiles may want to go into their own page within the developer options.
+     */
+    private void handleQsTileLongPressActionIfAny() {
+        Intent intent = getActivity().getIntent();
+        if (intent == null || !TextUtils.equals(ACTION_QS_TILE_PREFERENCES, intent.getAction())) {
+            return;
+        }
+
+        Log.d(TAG, "Developer options started from qstile long-press");
+        final ComponentName componentName = (ComponentName) intent.getParcelableExtra(
+                Intent.EXTRA_COMPONENT_NAME);
+        if (componentName == null) {
+            return;
+        }
+
+        if (DevelopmentTiles.WirelessDebugging.class.getName().equals(
+                componentName.getClassName()) && getDevelopmentOptionsController(
+                    WirelessDebuggingPreferenceController.class).isAvailable()) {
+            Log.d(TAG, "Long press from wireless debugging qstile");
+            new SubSettingLauncher(getContext())
+                    .setDestination(WirelessDebuggingFragment.class.getName())
+                    .setSourceMetricsCategory(SettingsEnums.SETTINGS_ADB_WIRELESS)
+                    .launch();
+        }
+        // Add other qstiles here
+    }
+
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index bfa43d1..468d8c5 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -34,6 +34,7 @@
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.Utils;
 import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -50,6 +51,8 @@
     static final String KEY_BATTERY_HEADER = "battery_header";
 
     @VisibleForTesting
+    BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
+    @VisibleForTesting
     BatteryMeterView mBatteryMeterView;
     @VisibleForTesting
     TextView mBatteryPercentText;
@@ -66,6 +69,8 @@
     public BatteryHeaderPreferenceController(Context context, String key) {
         super(context, key);
         mPowerManager = context.getSystemService(PowerManager.class);
+        mBatteryStatusFeatureProvider = FeatureFactory.getFactory(context)
+                .getBatteryStatusFeatureProvider(context);
     }
 
     public void setActivity(Activity activity) {
@@ -107,10 +112,12 @@
 
     public void updateHeaderPreference(BatteryInfo info) {
         mBatteryPercentText.setText(formatBatteryPercentageText(info.batteryLevel));
-        if (info.remainingLabel == null) {
-            mSummary1.setText(info.statusLabel);
-        } else {
-            mSummary1.setText(info.remainingLabel);
+        if (!mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info)) {
+            if (info.remainingLabel == null) {
+                mSummary1.setText(info.statusLabel);
+            } else {
+                mSummary1.setText(info.remainingLabel);
+            }
         }
 
         mBatteryMeterView.setBatteryLevel(info.batteryLevel);
@@ -118,6 +125,13 @@
         mBatteryMeterView.setPowerSave(mPowerManager.isPowerSaveMode());
     }
 
+    /**
+     * Callback which receives text for the summary line.
+     */
+    public void updateBatteryStatus(String statusLabel) {
+        mSummary1.setText(statusLabel);
+    }
+
     public void quickUpdateHeaderPreference() {
         Intent batteryBroadcast = mContext.registerReceiver(null,
                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
diff --git a/src/com/android/settings/fuelgauge/BatteryStatusFeatureProvider.java b/src/com/android/settings/fuelgauge/BatteryStatusFeatureProvider.java
new file mode 100644
index 0000000..c3fc247
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryStatusFeatureProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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.fuelgauge;
+
+/**
+ * Feature Provider used to retrieve battery status
+ */
+public interface BatteryStatusFeatureProvider {
+
+    /**
+     * Trigger a battery status update; return false if built-in status should be used.
+     */
+    boolean triggerBatteryStatusUpdate(
+            BatteryHeaderPreferenceController batteryHeaderPreferenceController, BatteryInfo info);
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryStatusFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatteryStatusFeatureProviderImpl.java
new file mode 100644
index 0000000..47d376d
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryStatusFeatureProviderImpl.java
@@ -0,0 +1,37 @@
+/*
+ * 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.fuelgauge;
+
+import android.content.Context;
+
+/**
+ * Used to override battery status string in Battery Header.
+ */
+public class BatteryStatusFeatureProviderImpl implements BatteryStatusFeatureProvider {
+
+    protected Context mContext;
+
+    public BatteryStatusFeatureProviderImpl(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    @Override
+    public boolean triggerBatteryStatusUpdate(
+            BatteryHeaderPreferenceController batteryHeaderPreferenceController, BatteryInfo info) {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index 0075529..fc39b59 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -243,7 +243,7 @@
         final MetricsFeatureProvider metricsFeatureProvider =
                 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
 
-        //navigate back to the homepage, screen rotate or after card dismissal
+        // navigate back to the homepage, screen rotate or after card dismissal
         if (!mIsFirstLaunch) {
             onContextualCardUpdated(cardsToKeep.stream()
                     .collect(groupingBy(ContextualCard::getCardType)));
@@ -266,8 +266,17 @@
                     SettingsEnums.ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT,
                     SettingsEnums.SETTINGS_HOMEPAGE,
                     null /* key */, (int) loadTime /* value */);
+
+            // display a card on timeout if the one-card space is pre-allocated
+            if (!cards.isEmpty() && ContextualCardLoader.getCardCount(mContext) == 1) {
+                onContextualCardUpdated(cards.stream()
+                        .collect(groupingBy(ContextualCard::getCardType)));
+                metricsFeatureProvider.action(mContext,
+                        SettingsEnums.ACTION_CONTEXTUAL_CARD_SHOW,
+                        ContextualCardLogUtils.buildCardListLog(cards));
+            }
         }
-        //only log homepage display upon a fresh launch
+        // only log homepage display upon a fresh launch
         final long totalTime = System.currentTimeMillis() - mStartTime;
         metricsFeatureProvider.action(mContext,
                 SettingsEnums.ACTION_CONTEXTUAL_HOME_SHOW, (int) totalTime);
diff --git a/src/com/android/settings/media/MediaOutputIndicatorSlice.java b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
index 17c8ef8..305c7df 100644
--- a/src/com/android/settings/media/MediaOutputIndicatorSlice.java
+++ b/src/com/android/settings/media/MediaOutputIndicatorSlice.java
@@ -27,6 +27,7 @@
 import android.graphics.Bitmap;
 import android.media.session.MediaController;
 import android.net.Uri;
+import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.drawable.IconCompat;
@@ -62,8 +63,12 @@
                 com.android.internal.R.drawable.ic_settings_bluetooth);
         final CharSequence title = mContext.getString(R.string.media_output_label_title,
                 Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
+        final int requestCode = TextUtils.isEmpty(getWorker().getPackageName())
+                ? 0
+                : getWorker().getPackageName().hashCode();
         final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
-                0 /* requestCode */, getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT);
+                requestCode,
+                getMediaOutputSliceIntent(), FLAG_UPDATE_CURRENT);
         final SliceAction primarySliceAction = SliceAction.createDeeplink(
                 primaryActionIntent, icon, ListBuilder.ICON_IMAGE, title);
         @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java
index 94a4f50..6611e8d 100644
--- a/src/com/android/settings/media/MediaOutputSlice.java
+++ b/src/com/android/settings/media/MediaOutputSlice.java
@@ -106,10 +106,15 @@
             final MediaDevice connectedDevice = worker.getCurrentConnectedMediaDevice();
             if (devices.size() == 1) {
                 // Zero state
-                for (MediaDevice device : devices) {
-                    addRow(device, device, listBuilder);
+                final MediaDevice device = devices.iterator().next();
+                addRow(device, device, listBuilder);
+                // Add "pair new" only when local output device exists
+                final int type = device.getDeviceType();
+                if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
+                        || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
+                        || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
+                    listBuilder.addRow(getPairNewRow());
                 }
-                listBuilder.addRow(getPairNewRow());
             } else {
                 final boolean isTouched = worker.getIsTouched();
                 // Fix the last top device when user press device to transfer.
@@ -252,9 +257,11 @@
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                 .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
                         getWorker().getPackageName());
-
+        final int requestCode = TextUtils.isEmpty(getWorker().getPackageName())
+                ? 0
+                : getWorker().getPackageName().hashCode();
         return SliceAction.createDeeplink(
-                PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */),
+                PendingIntent.getActivity(mContext, requestCode, intent, 0 /* flags */),
                 IconCompat.createWithResource(mContext, R.drawable.ic_add_blue_24dp),
                 ListBuilder.ICON_IMAGE,
                 mContext.getText(R.string.add));
diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java
index 61a53f3..4198269 100644
--- a/src/com/android/settings/media/RemoteMediaSlice.java
+++ b/src/com/android/settings/media/RemoteMediaSlice.java
@@ -17,6 +17,7 @@
 package com.android.settings.media;
 
 import static android.app.slice.Slice.EXTRA_RANGE_VALUE;
+import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
 
 import static com.android.settings.slices.CustomSliceRegistry.REMOTE_MEDIA_SLICE_URI;
 
@@ -24,11 +25,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.net.Uri;
+import android.text.SpannableString;
 import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
 import androidx.slice.builders.ListBuilder;
@@ -59,6 +64,9 @@
 
     private MediaDeviceUpdateWorker mWorker;
 
+    @VisibleForTesting
+    MediaRouter2Manager mRouterManager;
+
     public RemoteMediaSlice(Context context) {
         mContext = context;
     }
@@ -80,6 +88,9 @@
             Log.e(TAG, "Unable to get the slice worker.");
             return listBuilder.build();
         }
+        if (mRouterManager == null) {
+            mRouterManager = MediaRouter2Manager.getInstance(mContext);
+        }
         // Only displaying remote devices
         final List<RoutingSessionInfo> infos = getWorker().getActiveRemoteMediaDevice();
         if (infos.isEmpty()) {
@@ -91,7 +102,6 @@
                 R.drawable.ic_volume_remote);
         // To create an empty icon to indent the row
         final IconCompat emptyIcon = createEmptyIcon();
-        int requestCode = 0;
         for (RoutingSessionInfo info : infos) {
             final int maxVolume = info.getVolumeMax();
             if (maxVolume <= 0) {
@@ -99,20 +109,32 @@
                         + maxVolume);
                 continue;
             }
+            final CharSequence appName = Utils.getApplicationLabel(
+                    mContext, info.getClientPackageName());
             final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
-                    Utils.getApplicationLabel(mContext, info.getClientPackageName()));
+                    appName);
             listBuilder.addInputRange(new InputRangeBuilder()
                     .setTitleItem(icon, ListBuilder.ICON_IMAGE)
                     .setTitle(castVolume)
-                    .setInputAction(getSliderInputAction(requestCode++, info.getId()))
+                    .setInputAction(getSliderInputAction(info.getId().hashCode(), info.getId()))
                     .setPrimaryAction(getSoundSettingAction(castVolume, icon, info.getId()))
                     .setMax(maxVolume)
                     .setValue(info.getVolume()));
+
+            final boolean isMediaOutputDisabled =
+                    Utils.isMediaOutputDisabled(mRouterManager, info.getClientPackageName());
+            final SpannableString spannableTitle = new SpannableString(
+                    TextUtils.isEmpty(appName) ? "" : appName);
+            spannableTitle.setSpan(new ForegroundColorSpan(
+                            Utils.getColorAttrDefaultColor(
+                                    mContext, android.R.attr.textColorSecondary)), 0,
+                    spannableTitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
             listBuilder.addRow(new ListBuilder.RowBuilder()
-                    .setTitle(outputTitle)
+                    .setTitle(isMediaOutputDisabled ? spannableTitle : outputTitle)
                     .setSubtitle(info.getName())
                     .setTitleItem(emptyIcon, ListBuilder.ICON_IMAGE)
-                    .setPrimaryAction(getMediaOutputSliceAction(info.getClientPackageName())));
+                    .setPrimaryAction(getMediaOutputSliceAction(
+                            info.getClientPackageName(), isMediaOutputDisabled)));
         }
         return listBuilder.build();
     }
@@ -145,15 +167,19 @@
         return primarySliceAction;
     }
 
-    private SliceAction getMediaOutputSliceAction(String packageName) {
+    private SliceAction getMediaOutputSliceAction(
+            String packageName, boolean isMediaOutputDisabled) {
         final Intent intent = new Intent()
-                .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+                .setAction(isMediaOutputDisabled
+                        ? ""
+                        : MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                 .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME, packageName);
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 R.drawable.ic_volume_remote);
+        final int requestCode = TextUtils.isEmpty(packageName) ? 0 : packageName.hashCode();
         final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext,
-                0 /* requestCode */, intent, 0 /* flags */);
+                requestCode, intent, 0 /* flags */);
         final SliceAction primarySliceAction = SliceAction.createDeeplink(
                 primaryActionIntent, icon, ListBuilder.ICON_IMAGE,
                 mContext.getString(R.string.media_output_label_title,
diff --git a/src/com/android/settings/network/AdaptiveConnectivityPreferenceController.java b/src/com/android/settings/network/AdaptiveConnectivityPreferenceController.java
new file mode 100644
index 0000000..33d1d5b
--- /dev/null
+++ b/src/com/android/settings/network/AdaptiveConnectivityPreferenceController.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * {@link BasePreferenceController} that shows Adaptive connectivity on/off state.
+ */
+public class AdaptiveConnectivityPreferenceController extends BasePreferenceController {
+
+    public AdaptiveConnectivityPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return mContext.getResources().getBoolean(R.bool.config_show_adaptive_connectivity)
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1) == 1
+                ? mContext.getString(R.string.switch_on_text)
+                : mContext.getString(R.string.switch_off_text);
+    }
+}
diff --git a/src/com/android/settings/network/AdaptiveConnectivitySettings.java b/src/com/android/settings/network/AdaptiveConnectivitySettings.java
new file mode 100644
index 0000000..5e1dc12
--- /dev/null
+++ b/src/com/android/settings/network/AdaptiveConnectivitySettings.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Adaptive connectivity is a feature which automatically manages network connections.
+ */
+@SearchIndexable
+public class AdaptiveConnectivitySettings extends DashboardFragment {
+
+    private static final String TAG = "AdaptiveConnectivitySettings";
+
+    private static final String KEY_ADAPTIVE_CONNECTIVITY_PREFERENCE = "adaptive_connectivity";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.ADAPTIVE_CONNECTIVITY_CATEGORY;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.adaptive_connectivity_settings;
+    }
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.adaptive_connectivity_settings);
+}
diff --git a/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceController.java b/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceController.java
new file mode 100644
index 0000000..e072b5c
--- /dev/null
+++ b/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceController.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * {@link TogglePreferenceController} that controls whether Adaptive connectivity option is enabled.
+ */
+public class AdaptiveConnectivityTogglePreferenceController extends TogglePreferenceController {
+
+    public AdaptiveConnectivityTogglePreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1) == 1;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+                isChecked ? 1 : 0);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
index 9339d68..99e69c7 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java
@@ -280,7 +280,8 @@
                 String.format("showEuiccSettings: esimIgnoredDevice: %b, enabledEsimUiByDefault: "
                         + "%b, euiccProvisioned: %b, inDeveloperMode: %b.",
                 esimIgnoredDevice, enabledEsimUiByDefault, euiccProvisioned, inDeveloperMode));
-        return (inDeveloperMode || euiccProvisioned
+        return (euiccProvisioned
+                || (!esimIgnoredDevice && inDeveloperMode)
                 || (!esimIgnoredDevice && enabledEsimUiByDefault
                         && isCurrentCountrySupported(context)));
     }
diff --git a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
index 3911fb8..78dfe51 100644
--- a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
+++ b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
@@ -55,6 +55,13 @@
         super.updateState(preference);
 
         if (preference != null) {
+            // This is necessary to ensure that setting the title to the spannable string returned
+            // by getFooterText will be accepted.  Internally, setTitle does an equality check on
+            // the spannable string being set to the text already set on the preference.  That
+            // equality check apparently only takes into account the raw text and not and spannables
+            // that are part of the text.  So we clear the title before applying the spannable
+            // footer to ensure it is accepted.
+            preference.setTitle("");
             preference.setTitle(getFooterText());
         }
     }
diff --git a/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java b/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java
index bed25cd..f75fd4b 100644
--- a/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java
+++ b/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java
@@ -64,6 +64,7 @@
         filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
         filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
         filter.addAction(AudioManager.MASTER_MUTE_CHANGED_ACTION);
+        filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
         return filter;
     }
 }
diff --git a/src/com/android/settings/notification/BubbleNotificationPreferenceController.java b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java
index 0fa480c..1c7b4df 100644
--- a/src/com/android/settings/notification/BubbleNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/BubbleNotificationPreferenceController.java
@@ -18,6 +18,7 @@
 
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -81,7 +82,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return AVAILABLE;
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        return am.isLowRamDevice() ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java b/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java
index f123c51..a99155f 100644
--- a/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceController.java
@@ -18,6 +18,7 @@
 
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.provider.Settings;
 
@@ -48,7 +49,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return AVAILABLE;
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        return am.isLowRamDevice() ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
     }
 
     private boolean areBubblesEnabled() {
diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java
index 6d3c96d..bb62a56 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.text.TextUtils;
 
@@ -57,6 +58,8 @@
 
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
+    @VisibleForTesting
+    MediaRouter2Manager mRouterManager;
 
     public RemoteVolumeGroupController(Context context, String preferenceKey) {
         super(context, preferenceKey);
@@ -65,6 +68,7 @@
             mLocalMediaManager.registerCallback(this);
             mLocalMediaManager.startScan();
         }
+        mRouterManager = MediaRouter2Manager.getInstance(context);
     }
 
     @Override
@@ -111,8 +115,10 @@
             if (mPreferenceCategory.findPreference(info.getId()) != null) {
                 continue;
             }
+            final CharSequence appName = Utils.getApplicationLabel(
+                    mContext, info.getClientPackageName());
             final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
-                    Utils.getApplicationLabel(mContext, info.getClientPackageName()));
+                    appName);
             // Add slider
             final RemoteVolumeSeekBarPreference seekBarPreference =
                     new RemoteVolumeSeekBarPreference(mContext);
@@ -125,10 +131,13 @@
             seekBarPreference.setIcon(R.drawable.ic_volume_remote);
             mPreferenceCategory.addPreference(seekBarPreference);
             // Add output indicator
+            final boolean isMediaOutputDisabled = Utils.isMediaOutputDisabled(
+                    mRouterManager, info.getClientPackageName());
             final Preference preference = new Preference(mContext);
             preference.setKey(SWITCHER_PREFIX + info.getId());
-            preference.setTitle(outputTitle);
+            preference.setTitle(isMediaOutputDisabled ? appName : outputTitle);
             preference.setSummary(info.getName());
+            preference.setEnabled(!isMediaOutputDisabled);
             mPreferenceCategory.addPreference(preference);
         }
     }
diff --git a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
index f7bf75f..b32f922 100644
--- a/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
+++ b/src/com/android/settings/notification/VolumeSeekBarPreferenceController.java
@@ -107,7 +107,10 @@
         return mHelper.getMinVolume(getAudioStream());
     }
 
-    protected abstract int getAudioStream();
+    /**
+     * @return the audio stream type
+     */
+    public abstract int getAudioStream();
 
     protected abstract int getMuteIcon();
 
diff --git a/src/com/android/settings/notification/app/BlockPreferenceController.java b/src/com/android/settings/notification/app/BlockPreferenceController.java
index f55ea8c..2738962 100644
--- a/src/com/android/settings/notification/app/BlockPreferenceController.java
+++ b/src/com/android/settings/notification/app/BlockPreferenceController.java
@@ -106,8 +106,11 @@
             // It's always safe to override the importance if it's meant to be blocked or if
             // it was blocked and we are unblocking it.
             if (blocked || originalImportance == IMPORTANCE_NONE) {
-                final int importance = blocked ? IMPORTANCE_NONE
-                        : isDefaultChannel() ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_DEFAULT;
+                final int importance = blocked
+                        ? IMPORTANCE_NONE
+                        : isDefaultChannel()
+                                ? IMPORTANCE_UNSPECIFIED
+                                : mChannel.getOriginalImportance();
                 mChannel.setImportance(importance);
                 saveChannel();
             }
diff --git a/src/com/android/settings/notification/app/BubblePreference.java b/src/com/android/settings/notification/app/BubblePreference.java
index 7e071ff..f0046d7 100644
--- a/src/com/android/settings/notification/app/BubblePreference.java
+++ b/src/com/android/settings/notification/app/BubblePreference.java
@@ -45,8 +45,6 @@
     private int mSelectedPreference;
 
     private Context mContext;
-    private Drawable mSelectedBackground;
-    private Drawable mUnselectedBackground;
 
     private ButtonViewHolder mBubbleAllButton;
     private ButtonViewHolder mBubbleSelectedButton;
@@ -72,8 +70,6 @@
         mHelper = new RestrictedPreferenceHelper(context, this, attrs);
         mHelper.useAdminDisabledSummary(true);
         mContext = context;
-        mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
-        mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
         setLayoutResource(R.layout.bubble_preference);
     }
 
@@ -167,7 +163,9 @@
         }
 
         void setSelected(Context context, boolean selected) {
-            mView.setBackground(selected ?  mSelectedBackground : mUnselectedBackground);
+            mView.setBackground(mContext.getDrawable(selected
+                ? R.drawable.button_border_selected
+                : R.drawable.button_border_unselected));
             mView.setSelected(selected);
 
             ColorStateList stateList = selected
diff --git a/src/com/android/settings/notification/app/BubblePreferenceController.java b/src/com/android/settings/notification/app/BubblePreferenceController.java
index 1aed156..8f452f7 100644
--- a/src/com/android/settings/notification/app/BubblePreferenceController.java
+++ b/src/com/android/settings/notification/app/BubblePreferenceController.java
@@ -19,6 +19,7 @@
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import android.app.ActivityManager;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.provider.Settings;
@@ -77,7 +78,7 @@
             if (isDefaultChannel()) {
                 return true;
             } else {
-                return mAppRow != null;
+                return mAppRow != null &&  mAppRow.bubblePreference != BUBBLE_PREFERENCE_NONE;
             }
         }
         return true;
@@ -139,7 +140,8 @@
     }
 
     private boolean isGloballyEnabled() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        return !am.isLowRamDevice() && Settings.Global.getInt(mContext.getContentResolver(),
                 NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
     }
 
diff --git a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
index 7519c23..236e628 100644
--- a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
+++ b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
@@ -20,6 +20,7 @@
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -99,7 +100,8 @@
     }
 
     private boolean isGloballyEnabled() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        return !am.isLowRamDevice() && Settings.Global.getInt(mContext.getContentResolver(),
                 NOTIFICATION_BUBBLES, ON) == ON;
     }
 }
diff --git a/src/com/android/settings/notification/app/ChannelListPreferenceController.java b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
index b19fc71..88d960d 100644
--- a/src/com/android/settings/notification/app/ChannelListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
@@ -23,16 +23,14 @@
 import android.app.NotificationChannelGroup;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.graphics.BlendMode;
-import android.graphics.BlendModeColorFilter;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceGroup;
@@ -53,7 +51,8 @@
 public class ChannelListPreferenceController extends NotificationPreferenceController {
 
     private static final String KEY = "channels";
-    private static String KEY_GENERAL_CATEGORY = "categories";
+    private static final String KEY_GENERAL_CATEGORY = "categories";
+    private static final String KEY_ZERO_CATEGORIES = "zeroCategories";
     public static final String ARG_FROM_SETTINGS = "fromSettings";
 
     private List<NotificationChannelGroup> mChannelGroupList;
@@ -102,62 +101,192 @@
                 if (mContext == null) {
                     return;
                 }
-                populateList();
+                updateFullList(mPreference, mChannelGroupList);
             }
         }.execute();
     }
 
-    private void populateList() {
-        // TODO: if preference has children, compare with newly loaded list
-        mPreference.removeAll();
-
-        if (mChannelGroupList.isEmpty()) {
-            PreferenceCategory groupCategory = new PreferenceCategory(mContext);
-            groupCategory.setTitle(R.string.notification_channels);
-            groupCategory.setKey(KEY_GENERAL_CATEGORY);
-            mPreference.addPreference(groupCategory);
-
-            Preference empty = new Preference(mContext);
-            empty.setTitle(R.string.no_channels);
-            empty.setEnabled(false);
-            groupCategory.addPreference(empty);
-        } else {
-            populateGroupList();
-        }
-    }
-
-    private void populateGroupList() {
-        for (NotificationChannelGroup group : mChannelGroupList) {
-            PreferenceCategory groupCategory = new PreferenceCategory(mContext);
-            groupCategory.setOrderingAsAdded(true);
-            mPreference.addPreference(groupCategory);
-            if (group.getId() == null) {
-                groupCategory.setTitle(R.string.notification_channels_other);
-                groupCategory.setKey(KEY_GENERAL_CATEGORY);
+    /**
+     * Update the preferences group to match the
+     * @param groupPrefsList
+     * @param channelGroups
+     */
+    void updateFullList(@NonNull PreferenceCategory groupPrefsList,
+                @NonNull List<NotificationChannelGroup> channelGroups) {
+        if (channelGroups.isEmpty()) {
+            if (groupPrefsList.getPreferenceCount() == 1
+                    && KEY_ZERO_CATEGORIES.equals(groupPrefsList.getPreference(0).getKey())) {
+                // Ensure the titles are correct for the current language, but otherwise leave alone
+                PreferenceGroup groupCategory = (PreferenceGroup) groupPrefsList.getPreference(0);
+                groupCategory.setTitle(R.string.notification_channels);
+                groupCategory.getPreference(0).setTitle(R.string.no_channels);
             } else {
-                groupCategory.setTitle(group.getName());
-                groupCategory.setKey(group.getId());
-                populateGroupToggle(groupCategory, group);
+                // Clear any contents and create the 'zero-categories' group.
+                groupPrefsList.removeAll();
+
+                PreferenceCategory groupCategory = new PreferenceCategory(mContext);
+                groupCategory.setTitle(R.string.notification_channels);
+                groupCategory.setKey(KEY_ZERO_CATEGORIES);
+                groupPrefsList.addPreference(groupCategory);
+
+                Preference empty = new Preference(mContext);
+                empty.setTitle(R.string.no_channels);
+                empty.setEnabled(false);
+                groupCategory.addPreference(empty);
             }
-            if (!group.isBlocked()) {
-                final List<NotificationChannel> channels = group.getChannels();
-                Collections.sort(channels, CHANNEL_COMPARATOR);
-                int N = channels.size();
-                for (int i = 0; i < N; i++) {
-                    final NotificationChannel channel = channels.get(i);
-                    // conversations get their own section
-                    if (TextUtils.isEmpty(channel.getConversationId()) || channel.isDemoted()) {
-                        populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
-                    }
-                }
+        } else {
+            updateGroupList(groupPrefsList, channelGroups);
+        }
+    }
+
+    /**
+     * Looks for the category for the given group's key at the expected index, if that doesn't
+     * match, it checks all groups, and if it can't find that group anywhere, it creates it.
+     */
+    @NonNull
+    private PreferenceCategory findOrCreateGroupCategoryForKey(
+            @NonNull PreferenceCategory groupPrefsList, @Nullable String key, int expectedIndex) {
+        if (key == null) {
+            key = KEY_GENERAL_CATEGORY;
+        }
+        int preferenceCount = groupPrefsList.getPreferenceCount();
+        if (expectedIndex < preferenceCount) {
+            Preference preference = groupPrefsList.getPreference(expectedIndex);
+            if (key.equals(preference.getKey())) {
+                return (PreferenceCategory) preference;
+            }
+        }
+        for (int i = 0; i < preferenceCount; i++) {
+            Preference preference = groupPrefsList.getPreference(i);
+            if (key.equals(preference.getKey())) {
+                preference.setOrder(expectedIndex);
+                return (PreferenceCategory) preference;
+            }
+        }
+        PreferenceCategory groupCategory = new PreferenceCategory(mContext);
+        groupCategory.setOrder(expectedIndex);
+        groupCategory.setKey(key);
+        groupPrefsList.addPreference(groupCategory);
+        return groupCategory;
+    }
+
+    private void updateGroupList(@NonNull PreferenceCategory groupPrefsList,
+            @NonNull List<NotificationChannelGroup> channelGroups) {
+        // Update the list, but optimize for the most common case where the list hasn't changed.
+        int numFinalGroups = channelGroups.size();
+        int initialPrefCount = groupPrefsList.getPreferenceCount();
+        List<PreferenceCategory> finalOrderedGroups = new ArrayList<>(numFinalGroups);
+        for (int i = 0; i < numFinalGroups; i++) {
+            NotificationChannelGroup group = channelGroups.get(i);
+            PreferenceCategory groupCategory =
+                    findOrCreateGroupCategoryForKey(groupPrefsList, group.getId(), i);
+            finalOrderedGroups.add(groupCategory);
+            updateGroupPreferences(group, groupCategory);
+        }
+        int postAddPrefCount = groupPrefsList.getPreferenceCount();
+        // If any groups were inserted (into a non-empty list) or need to be removed, we need to
+        // remove all groups and re-add them all.
+        // This is required to ensure proper ordering of inserted groups, and it simplifies logic
+        // at the cost of computation in the rare case that the list is changing.
+        boolean hasInsertions = initialPrefCount != 0 && initialPrefCount != numFinalGroups;
+        boolean requiresRemoval = postAddPrefCount != numFinalGroups;
+        if (hasInsertions || requiresRemoval) {
+            groupPrefsList.removeAll();
+            for (PreferenceCategory group : finalOrderedGroups) {
+                groupPrefsList.addPreference(group);
             }
         }
     }
 
-    protected void populateGroupToggle(final PreferenceGroup parent,
-            NotificationChannelGroup group) {
-        RestrictedSwitchPreference preference =
-                new RestrictedSwitchPreference(mContext);
+    /**
+     * Looks for the channel preference for the given channel's key at the expected index, if that
+     * doesn't match, it checks all rows, and if it can't find that channel anywhere, it creates
+     * the preference.
+     */
+    @NonNull
+    private MasterSwitchPreference findOrCreateChannelPrefForKey(
+            @NonNull PreferenceGroup groupPrefGroup, @NonNull String key, int expectedIndex) {
+        int preferenceCount = groupPrefGroup.getPreferenceCount();
+        if (expectedIndex < preferenceCount) {
+            Preference preference = groupPrefGroup.getPreference(expectedIndex);
+            if (key.equals(preference.getKey())) {
+                return (MasterSwitchPreference) preference;
+            }
+        }
+        for (int i = 0; i < preferenceCount; i++) {
+            Preference preference = groupPrefGroup.getPreference(i);
+            if (key.equals(preference.getKey())) {
+                preference.setOrder(expectedIndex);
+                return (MasterSwitchPreference) preference;
+            }
+        }
+        MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext);
+        channelPref.setOrder(expectedIndex);
+        channelPref.setKey(key);
+        groupPrefGroup.addPreference(channelPref);
+        return channelPref;
+    }
+
+    private void updateGroupPreferences(@NonNull NotificationChannelGroup group,
+            @NonNull PreferenceGroup groupPrefGroup) {
+        int initialPrefCount = groupPrefGroup.getPreferenceCount();
+        List<Preference> finalOrderedPrefs = new ArrayList<>();
+        if (group.getId() == null) {
+            // For the 'null' group, set the "Other" title.
+            groupPrefGroup.setTitle(R.string.notification_channels_other);
+        } else {
+            // For an app-defined group, set their name and create a row to toggle 'isBlocked'.
+            groupPrefGroup.setTitle(group.getName());
+            finalOrderedPrefs.add(addOrUpdateGroupToggle(groupPrefGroup, group));
+        }
+        // Here "empty" means having no channel rows; the group toggle is ignored for this purpose.
+        boolean initiallyEmpty = groupPrefGroup.getPreferenceCount() == finalOrderedPrefs.size();
+
+        // For each channel, add or update the preference object.
+        final List<NotificationChannel> channels =
+                group.isBlocked() ? Collections.emptyList() : group.getChannels();
+        Collections.sort(channels, CHANNEL_COMPARATOR);
+        for (NotificationChannel channel : channels) {
+            if (!TextUtils.isEmpty(channel.getConversationId()) && !channel.isDemoted()) {
+                // conversations get their own section
+                continue;
+            }
+            // Get or create the row, and populate its current state.
+            MasterSwitchPreference channelPref = findOrCreateChannelPrefForKey(groupPrefGroup,
+                    channel.getId(), /* expectedIndex */ finalOrderedPrefs.size());
+            updateSingleChannelPrefs(channelPref, channel, group.isBlocked());
+            finalOrderedPrefs.add(channelPref);
+        }
+        int postAddPrefCount = groupPrefGroup.getPreferenceCount();
+
+        // If any channels were inserted (into a non-empty list) or need to be removed, we need to
+        // remove all preferences and re-add them all.
+        // This is required to ensure proper ordering of inserted channels, and it simplifies logic
+        // at the cost of computation in the rare case that the list is changing.
+        int numFinalGroups = finalOrderedPrefs.size();
+        boolean hasInsertions = !initiallyEmpty && initialPrefCount != numFinalGroups;
+        boolean requiresRemoval = postAddPrefCount != numFinalGroups;
+        if (hasInsertions || requiresRemoval) {
+            groupPrefGroup.removeAll();
+            for (Preference preference : finalOrderedPrefs) {
+                groupPrefGroup.addPreference(preference);
+            }
+        }
+    }
+
+    /** Add or find and update the toggle for disabling the entire notification channel group. */
+    private Preference addOrUpdateGroupToggle(@NonNull final PreferenceGroup parent,
+            @NonNull final NotificationChannelGroup group) {
+        boolean shouldAdd = false;
+        final RestrictedSwitchPreference preference;
+        if (parent.getPreferenceCount() > 0
+                && parent.getPreference(0) instanceof RestrictedSwitchPreference) {
+            preference = (RestrictedSwitchPreference) parent.getPreference(0);
+        } else {
+            shouldAdd = true;
+            preference = new RestrictedSwitchPreference(mContext);
+        }
+        preference.setOrder(-1);
         preference.setTitle(mContext.getString(
                 R.string.notification_switch_label, group.getName()));
         preference.setEnabled(mAdmin == null
@@ -171,23 +300,26 @@
             onGroupBlockStateChanged(group);
             return true;
         });
-
-        parent.addPreference(preference);
+        if (shouldAdd) {
+            parent.addPreference(preference);
+        }
+        return preference;
     }
 
-    protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
-            final NotificationChannel channel, final boolean groupBlocked) {
-        MasterSwitchPreference channelPref = new MasterSwitchPreference(mContext);
+    /** Update the properties of the channel preference with the values from the channel object. */
+    private void updateSingleChannelPrefs(@NonNull final MasterSwitchPreference channelPref,
+            @NonNull final NotificationChannel channel,
+            final boolean groupBlocked) {
         channelPref.setSwitchEnabled(mAdmin == null
                 && isChannelBlockable(channel)
                 && isChannelConfigurable(channel)
                 && !groupBlocked);
-        channelPref.setIcon(null);
         if (channel.getImportance() > IMPORTANCE_LOW) {
             channelPref.setIcon(getAlertingIcon());
+        } else {
+            channelPref.setIcon(null);
         }
         channelPref.setIconSize(MasterSwitchPreference.ICON_SIZE_SMALL);
-        channelPref.setKey(channel.getId());
         channelPref.setTitle(channel.getName());
         channelPref.setSummary(NotificationBackend.getSentSummary(
                 mContext, mAppRow.sentByChannel.get(channel.getId()), false));
@@ -207,25 +339,18 @@
         channelPref.setOnPreferenceChangeListener(
                 (preference, o) -> {
                     boolean value = (Boolean) o;
-                    int importance = value ? IMPORTANCE_LOW : IMPORTANCE_NONE;
+                    int importance = value ? channel.getOriginalImportance() : IMPORTANCE_NONE;
                     channel.setImportance(importance);
-                    channel.lockFields(
-                            NotificationChannel.USER_LOCKED_IMPORTANCE);
+                    channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
                     MasterSwitchPreference channelPref1 = (MasterSwitchPreference) preference;
                     channelPref1.setIcon(null);
                     if (channel.getImportance() > IMPORTANCE_LOW) {
                         channelPref1.setIcon(getAlertingIcon());
                     }
-                    toggleBehaviorIconState(channelPref1.getIcon(),
-                            importance != IMPORTANCE_NONE);
                     mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, channel);
 
                     return true;
                 });
-        if (parent.findPreference(channelPref.getKey()) == null) {
-            parent.addPreference(channelPref);
-        }
-        return channelPref;
     }
 
     private Drawable getAlertingIcon() {
@@ -234,52 +359,13 @@
         return icon;
     }
 
-    private void toggleBehaviorIconState(Drawable icon, boolean enabled) {
-        if (icon == null) return;
-
-        LayerDrawable layerDrawable = (LayerDrawable) icon;
-        GradientDrawable background =
-                (GradientDrawable) layerDrawable.findDrawableByLayerId(R.id.back);
-
-        if (background == null) return;
-
-        if (enabled) {
-            background.clearColorFilter();
-        } else {
-            background.setColorFilter(new BlendModeColorFilter(
-                    mContext.getColor(R.color.material_grey_300),
-                    BlendMode.SRC_IN));
-        }
-    }
-
     protected void onGroupBlockStateChanged(NotificationChannelGroup group) {
         if (group == null) {
             return;
         }
-        PreferenceGroup groupGroup = mPreference.findPreference(group.getId());
-
-        if (groupGroup != null) {
-            if (group.isBlocked()) {
-                List<Preference> toRemove = new ArrayList<>();
-                int childCount = groupGroup.getPreferenceCount();
-                for (int i = 0; i < childCount; i++) {
-                    Preference pref = groupGroup.getPreference(i);
-                    if (pref instanceof MasterSwitchPreference) {
-                        toRemove.add(pref);
-                    }
-                }
-                for (Preference pref : toRemove) {
-                    groupGroup.removePreference(pref);
-                }
-            } else {
-                final List<NotificationChannel> channels = group.getChannels();
-                Collections.sort(channels, CHANNEL_COMPARATOR);
-                int N = channels.size();
-                for (int i = 0; i < N; i++) {
-                    final NotificationChannel channel = channels.get(i);
-                    populateSingleChannelPrefs(groupGroup, channel, group.isBlocked());
-                }
-            }
+        PreferenceGroup groupPrefGroup = mPreference.findPreference(group.getId());
+        if (groupPrefGroup != null) {
+            updateGroupPreferences(group, groupPrefGroup);
         }
     }
 }
diff --git a/src/com/android/settings/notification/app/ConversationPriorityPreference.java b/src/com/android/settings/notification/app/ConversationPriorityPreference.java
index 307abec..67bffbf 100644
--- a/src/com/android/settings/notification/app/ConversationPriorityPreference.java
+++ b/src/com/android/settings/notification/app/ConversationPriorityPreference.java
@@ -50,8 +50,6 @@
     private View mAlertButton;
     private View mPriorityButton;
     private Context mContext;
-    Drawable selectedBackground;
-    Drawable unselectedBackground;
     private static final int BUTTON_ANIM_TIME_MS = 100;
 
     public ConversationPriorityPreference(Context context, AttributeSet attrs,
@@ -77,8 +75,6 @@
 
     private void init(Context context) {
         mContext = context;
-        selectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
-        unselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
         setLayoutResource(R.layout.notif_priority_conversation_preference);
     }
 
@@ -148,86 +144,43 @@
             TransitionManager.beginDelayedTransition(parent, transition);
         }
 
-        ColorStateList colorAccent = getAccentTint();
-        ColorStateList colorNormal = getRegularTint();
-        ImageView silenceIcon = parent.findViewById(R.id.silence_icon);
-        TextView silenceLabel = parent.findViewById(R.id.silence_label);
-        TextView silenceSummary = parent.findViewById(R.id.silence_summary);
-        ImageView alertIcon = parent.findViewById(R.id.alert_icon);
-        TextView alertLabel = parent.findViewById(R.id.alert_label);
-        TextView alertSummary = parent.findViewById(R.id.alert_summary);
-        ImageView priorityIcon = parent.findViewById(R.id.priority_icon);
-        TextView priorityLabel = parent.findViewById(R.id.priority_label);
-        TextView prioritySummary = parent.findViewById(R.id.priority_summary);
-
         if (importance <= IMPORTANCE_LOW && importance > IMPORTANCE_UNSPECIFIED) {
-            alertSummary.setVisibility(GONE);
-            alertIcon.setImageTintList(colorNormal);
-            alertLabel.setTextColor(colorNormal);
-
-            prioritySummary.setVisibility(GONE);
-            priorityIcon.setImageTintList(colorNormal);
-            priorityLabel.setTextColor(colorNormal);
-
-            silenceIcon.setImageTintList(colorAccent);
-            silenceLabel.setTextColor(colorAccent);
-            silenceSummary.setVisibility(VISIBLE);
-
-            mAlertButton.setBackground(unselectedBackground);
-            mPriorityButton.setBackground(unselectedBackground);
-            mSilenceButton.setBackground(selectedBackground);
-            // a11y service won't always read the newly appearing text in the right order if the
-            // selection happens too soon (readback happens on a different thread as layout). post
-            // the selection to make that conflict less likely
-            parent.post(() -> {
-                mSilenceButton.setSelected(true);
-                mAlertButton.setSelected(false);
-                mPriorityButton.setSelected(false);
-            });
+            setSelected(mPriorityButton, false);
+            setSelected(mAlertButton, false);
+            setSelected(mSilenceButton, true);
         } else {
             if (isPriority) {
-                alertSummary.setVisibility(GONE);
-                alertIcon.setImageTintList(colorNormal);
-                alertLabel.setTextColor(colorNormal);
-
-                prioritySummary.setVisibility(VISIBLE);
-                priorityIcon.setImageTintList(colorAccent);
-                priorityLabel.setTextColor(colorAccent);
-
-                silenceIcon.setImageTintList(colorNormal);
-                silenceLabel.setTextColor(colorNormal);
-                silenceSummary.setVisibility(GONE);
-
-                mAlertButton.setBackground(unselectedBackground);
-                mPriorityButton.setBackground(selectedBackground);
-                mSilenceButton.setBackground(unselectedBackground);
-                parent.post(() -> {
-                    mSilenceButton.setSelected(false);
-                    mAlertButton.setSelected(false);
-                    mPriorityButton.setSelected(true);
-                });
+                setSelected(mPriorityButton, true);
+                setSelected(mAlertButton, false);
+                setSelected(mSilenceButton, false);
             } else {
-                alertSummary.setVisibility(VISIBLE);
-                alertIcon.setImageTintList(colorAccent);
-                alertLabel.setTextColor(colorAccent);
-
-                prioritySummary.setVisibility(GONE);
-                priorityIcon.setImageTintList(colorNormal);
-                priorityLabel.setTextColor(colorNormal);
-
-                silenceIcon.setImageTintList(colorNormal);
-                silenceLabel.setTextColor(colorNormal);
-                silenceSummary.setVisibility(GONE);
-
-                mAlertButton.setBackground(selectedBackground);
-                mPriorityButton.setBackground(unselectedBackground);
-                mSilenceButton.setBackground(unselectedBackground);
-                parent.post(() -> {
-                    mSilenceButton.setSelected(false);
-                    mAlertButton.setSelected(true);
-                    mPriorityButton.setSelected(false);
-                });
+                setSelected(mPriorityButton, false);
+                setSelected(mAlertButton, true);
+                setSelected(mSilenceButton, false);
             }
         }
     }
+
+    void setSelected(View view, boolean selected) {
+        ColorStateList colorAccent = getAccentTint();
+        ColorStateList colorNormal = getRegularTint();
+
+        ImageView icon = view.findViewById(R.id.icon);
+        TextView label = view.findViewById(R.id.label);
+        TextView summary = view.findViewById(R.id.summary);
+
+        icon.setImageTintList(selected ? colorAccent : colorNormal);
+        label.setTextColor(selected ? colorAccent : colorNormal);
+        summary.setVisibility(selected ? VISIBLE : GONE);
+
+        view.setBackground(mContext.getDrawable(selected
+                ? R.drawable.button_border_selected
+                : R.drawable.button_border_unselected));
+        // a11y service won't always read the newly appearing text in the right order if the
+        // selection happens too soon (readback happens on a different thread as layout). post
+        // the selection to make that conflict less likely
+        view.post(() -> {
+            view.setSelected(selected);
+        });
+    }
 }
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
index 7635a4f..b184740 100644
--- a/src/com/android/settings/notification/history/NotificationHistoryActivity.java
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -155,21 +155,22 @@
 
             final View container = viewForPackage.findViewById(R.id.notification_list);
             container.setVisibility(View.GONE);
-            ImageButton expand = viewForPackage.findViewById(R.id.expand);
-            expand.setContentDescription(container.getVisibility() == View.VISIBLE
+            View header = viewForPackage.findViewById(R.id.app_header);
+            ImageView expand = viewForPackage.findViewById(R.id.expand);
+            header.setStateDescription(container.getVisibility() == View.VISIBLE
                     ? getString(R.string.condition_expand_hide)
                     : getString(R.string.condition_expand_show));
             int finalI = i;
-            expand.setOnClickListener(v -> {
+            header.setOnClickListener(v -> {
                 container.setVisibility(container.getVisibility() == View.VISIBLE
                         ? View.GONE : View.VISIBLE);
                 expand.setImageResource(container.getVisibility() == View.VISIBLE
                         ? R.drawable.ic_expand_less
                         : com.android.internal.R.drawable.ic_expand_more);
-                expand.setContentDescription(container.getVisibility() == View.VISIBLE
+                header.setStateDescription(container.getVisibility() == View.VISIBLE
                         ? getString(R.string.condition_expand_hide)
                         : getString(R.string.condition_expand_show));
-                expand.sendAccessibilityEvent(TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+                header.sendAccessibilityEvent(TYPE_VIEW_ACCESSIBILITY_FOCUSED);
                 mUiEventLogger.logWithPosition(
                         (container.getVisibility() == View.VISIBLE)
                             ? NotificationHistoryEvent.NOTIFICATION_HISTORY_PACKAGE_HISTORY_OPEN
diff --git a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
index 4991548..da1ed11 100644
--- a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
+++ b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
@@ -33,7 +33,6 @@
     NotificationHistoryViewHolder(View itemView) {
         super(itemView);
         mTime = itemView.findViewById(R.id.timestamp);
-        mTime.setShowRelativeTime(true);
         mTitle = itemView.findViewById(R.id.title);
         mSummary = itemView.findViewById(R.id.text);
     }
diff --git a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
index de12847..ac39f88 100644
--- a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
+++ b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
@@ -108,7 +108,7 @@
                                 : NotificationHistoryActivity.NotificationHistoryEvent
                                 .NOTIFICATION_HISTORY_RECENT_ITEM_CLICK,
                         uid, pkg, instanceId, position);
-                if (pi != null) {
+                if (pi != null && isPendingIntentValid) {
                     try {
                         pi.send();
                     } catch (PendingIntent.CanceledException e) {
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index afbce09..b9aa68a 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -31,6 +31,7 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
@@ -103,6 +104,12 @@
 
     public abstract PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context);
 
+    /**
+     * Retrieve implementation for Battery Status feature.
+     */
+    public abstract BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(
+            Context context);
+
     public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
 
     public abstract DockUpdaterFeatureProvider getDockUpdaterFeatureProvider();
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 29beb5b..d4d396f 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -42,6 +42,8 @@
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl;
+import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
+import com.android.settings.fuelgauge.BatteryStatusFeatureProviderImpl;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProviderImpl;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -78,6 +80,7 @@
     private SecurityFeatureProvider mSecurityFeatureProvider;
     private SuggestionFeatureProvider mSuggestionFeatureProvider;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+    private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
     private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
     private UserFeatureProvider mUserFeatureProvider;
     private SlicesFeatureProvider mSlicesFeatureProvider;
@@ -111,6 +114,15 @@
     }
 
     @Override
+    public BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(Context context) {
+        if (mBatteryStatusFeatureProvider == null) {
+            mBatteryStatusFeatureProvider = new BatteryStatusFeatureProviderImpl(
+                    context.getApplicationContext());
+        }
+        return mBatteryStatusFeatureProvider;
+    }
+
+    @Override
     public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
         if (mDashboardFeatureProvider == null) {
             mDashboardFeatureProvider = new DashboardFeatureProviderImpl(
diff --git a/src/com/android/settings/panel/MediaOutputPanel.java b/src/com/android/settings/panel/MediaOutputPanel.java
index 6a296b7..7caf7dd 100644
--- a/src/com/android/settings/panel/MediaOutputPanel.java
+++ b/src/com/android/settings/panel/MediaOutputPanel.java
@@ -25,11 +25,7 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
@@ -109,8 +105,7 @@
     @Override
     public IconCompat getIcon() {
         if (mMediaController == null) {
-            return IconCompat.createWithResource(mContext, R.drawable.ic_media_stream).setTint(
-                    Utils.getColorAccentDefaultColor(mContext));
+            return null;
         }
         final MediaMetadata metadata = mMediaController.getMetadata();
         if (metadata != null) {
@@ -124,25 +119,6 @@
             }
         }
         Log.d(TAG, "Media meta data does not contain icon information");
-        return getPackageIcon();
-    }
-
-    private IconCompat getPackageIcon() {
-        try {
-            final Drawable drawable = mContext.getPackageManager().getApplicationIcon(mPackageName);
-            if (drawable instanceof BitmapDrawable) {
-                return IconCompat.createWithBitmap(((BitmapDrawable) drawable).getBitmap());
-            }
-            final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
-                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-            final Canvas canvas = new Canvas(bitmap);
-            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-            drawable.draw(canvas);
-
-            return IconCompat.createWithBitmap(bitmap);
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Package is not found. Unable to get package icon.");
-        }
         return null;
     }
 
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index 1541cfd..7db312d 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -93,10 +93,12 @@
     private String mPanelClosedKey;
     private LinearLayout mPanelHeader;
     private ImageView mTitleIcon;
+    private LinearLayout mTitleGroup;
     private TextView mHeaderTitle;
     private TextView mHeaderSubtitle;
     private int mMaxHeight;
     private View mFooterDivider;
+    private boolean mPanelCreating;
 
     private final Map<Uri, LiveData<Slice>> mSliceLiveData = new LinkedHashMap<>();
 
@@ -127,6 +129,7 @@
                     if (mPanelSlices != null) {
                         mPanelSlices.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                     }
+                    mPanelCreating = false;
                 }
             };
 
@@ -140,6 +143,7 @@
         mLayoutView.getViewTreeObserver()
                 .addOnGlobalLayoutListener(mPanelLayoutListener);
         mMaxHeight = getResources().getDimensionPixelSize(R.dimen.output_switcher_slice_max_height);
+        mPanelCreating = true;
         createPanelContent();
         return mLayoutView;
     }
@@ -153,6 +157,7 @@
      * Call createPanelContent() once animation end.
      */
     void updatePanelWithAnimation() {
+        mPanelCreating = true;
         final View panelContent = mLayoutView.findViewById(R.id.panel_container);
         final AnimatorSet animatorSet = buildAnimatorSet(mLayoutView,
                 0.0f /* startY */, panelContent.getHeight() /* endY */,
@@ -171,6 +176,10 @@
         animatorSet.start();
     }
 
+    boolean isPanelCreating() {
+        return mPanelCreating;
+    }
+
     private void createPanelContent() {
         final FragmentActivity activity = getActivity();
         if (activity == null) {
@@ -181,6 +190,7 @@
             activity.finish();
             return;
         }
+
         final ViewGroup.LayoutParams params = mLayoutView.getLayoutParams();
         params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
         mLayoutView.setLayoutParams(params);
@@ -191,6 +201,7 @@
         mTitleView = mLayoutView.findViewById(R.id.panel_title);
         mPanelHeader = mLayoutView.findViewById(R.id.panel_header);
         mTitleIcon = mLayoutView.findViewById(R.id.title_icon);
+        mTitleGroup = mLayoutView.findViewById(R.id.title_group);
         mHeaderTitle = mLayoutView.findViewById(R.id.header_title);
         mHeaderSubtitle = mLayoutView.findViewById(R.id.header_subtitle);
         mFooterDivider = mLayoutView.findViewById(R.id.footer_divider);
@@ -228,26 +239,13 @@
 
         final IconCompat icon = mPanel.getIcon();
         final CharSequence title = mPanel.getTitle();
-        if (icon == null) {
+
+        if (icon != null || mPanel.getViewType() == PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON) {
+            enablePanelHeader(icon, title);
+        } else {
             mTitleView.setVisibility(View.VISIBLE);
             mPanelHeader.setVisibility(View.GONE);
             mTitleView.setText(title);
-        } else {
-            mTitleView.setVisibility(View.GONE);
-            mPanelHeader.setVisibility(View.VISIBLE);
-            mPanelHeader.setAccessibilityPaneTitle(title);
-            mTitleIcon.setImageIcon(icon.toIcon(getContext()));
-            mHeaderTitle.setText(title);
-            mHeaderSubtitle.setText(mPanel.getSubTitle());
-            if (mPanel.getHeaderIconIntent() != null) {
-                mTitleIcon.setOnClickListener(getHeaderIconListener());
-                mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-            } else {
-                final int size = getResources().getDimensionPixelSize(
-                        R.dimen.output_switcher_panel_icon_size);
-                mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
-            }
         }
 
         if (mPanel.getViewType() == PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON) {
@@ -281,6 +279,29 @@
                 0 /* value */);
     }
 
+    private void enablePanelHeader(IconCompat icon, CharSequence title) {
+        mTitleView.setVisibility(View.GONE);
+        mPanelHeader.setVisibility(View.VISIBLE);
+        mPanelHeader.setAccessibilityPaneTitle(title);
+        mHeaderTitle.setText(title);
+        mHeaderSubtitle.setText(mPanel.getSubTitle());
+        if (icon != null) {
+            mTitleGroup.setVisibility(View.VISIBLE);
+            mTitleIcon.setImageIcon(icon.toIcon(getContext()));
+            if (mPanel.getHeaderIconIntent() != null) {
+                mTitleIcon.setOnClickListener(getHeaderIconListener());
+                mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+            } else {
+                final int size = getResources().getDimensionPixelSize(
+                        R.dimen.output_switcher_panel_icon_size);
+                mTitleIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
+            }
+        } else {
+            mTitleGroup.setVisibility(View.GONE);
+        }
+    }
+
     private void loadAllSlices() {
         mSliceLiveData.clear();
         final List<Uri> sliceUris = mPanel.getSlices();
@@ -477,7 +498,13 @@
         @Override
         public void onHeaderChanged() {
             ThreadUtils.postOnMainThread(() -> {
-                mTitleIcon.setImageIcon(mPanel.getIcon().toIcon(getContext()));
+                final IconCompat icon = mPanel.getIcon();
+                if (icon != null) {
+                    mTitleIcon.setImageIcon(icon.toIcon(getContext()));
+                    mTitleGroup.setVisibility(View.VISIBLE);
+                } else {
+                    mTitleGroup.setVisibility(View.GONE);
+                }
                 mHeaderTitle.setText(mPanel.getTitle());
                 mHeaderSubtitle.setText(mPanel.getSubTitle());
             });
diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java
index 68cb8d5..b7b1519 100644
--- a/src/com/android/settings/panel/SettingsPanelActivity.java
+++ b/src/com/android/settings/panel/SettingsPanelActivity.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.Window;
@@ -41,12 +42,14 @@
  */
 public class SettingsPanelActivity extends FragmentActivity {
 
-    private final String TAG = "panel_activity";
+    private static final String TAG = "SettingsPanelActivity";
 
     @VisibleForTesting
     final Bundle mBundle = new Bundle();
     @VisibleForTesting
     boolean mForceCreation = false;
+    @VisibleForTesting
+    PanelFragment mPanelFragment;
 
     /**
      * Key specifying which Panel the app is requesting.
@@ -87,7 +90,9 @@
     @Override
     protected void onStop() {
         super.onStop();
-        mForceCreation = true;
+        if (mPanelFragment != null && !mPanelFragment.isPanelCreating()) {
+            mForceCreation = true;
+        }
     }
 
     @Override
@@ -104,10 +109,10 @@
             return;
         }
 
+        final String action = callingIntent.getAction();
         // We will use it once media output switch panel support remote device.
         final String mediaPackageName = callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
-
-        mBundle.putString(KEY_PANEL_TYPE_ARGUMENT, callingIntent.getAction());
+        mBundle.putString(KEY_PANEL_TYPE_ARGUMENT, action);
         mBundle.putString(KEY_CALLING_PACKAGE_NAME, getCallingPackage());
         mBundle.putString(KEY_MEDIA_PACKAGE_NAME, mediaPackageName);
 
@@ -116,9 +121,21 @@
 
         // If fragment already exists and visible, we will need to update panel with animation.
         if (!shouldForceCreation && fragment != null && fragment instanceof PanelFragment) {
-            final PanelFragment panelFragment = (PanelFragment) fragment;
-            panelFragment.setArguments(mBundle);
-            panelFragment.updatePanelWithAnimation();
+            mPanelFragment = (PanelFragment) fragment;
+            if (mPanelFragment.isPanelCreating()) {
+                Log.w(TAG, "A panel is creating, skip " + action);
+                return;
+            }
+
+            final Bundle bundle = fragment.getArguments();
+            if (bundle != null
+                    && TextUtils.equals(action, bundle.getString(KEY_PANEL_TYPE_ARGUMENT))) {
+                Log.w(TAG, "Panel is showing the same action, skip " + action);
+                return;
+            }
+
+            mPanelFragment.setArguments(new Bundle(mBundle));
+            mPanelFragment.updatePanelWithAnimation();
         } else {
             setContentView(R.layout.settings_panel);
 
@@ -127,9 +144,9 @@
             window.setGravity(Gravity.BOTTOM);
             window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                     WindowManager.LayoutParams.WRAP_CONTENT);
-            final PanelFragment panelFragment = new PanelFragment();
-            panelFragment.setArguments(mBundle);
-            fragmentManager.beginTransaction().add(R.id.main_content, panelFragment).commit();
+            mPanelFragment = new PanelFragment();
+            mPanelFragment.setArguments(new Bundle(mBundle));
+            fragmentManager.beginTransaction().add(R.id.main_content, mPanelFragment).commit();
         }
     }
 }
diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java
index ae36f35..6bd4c24 100644
--- a/src/com/android/settings/sim/SimSelectNotification.java
+++ b/src/com/android/settings/sim/SimSelectNotification.java
@@ -20,6 +20,7 @@
 import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
 import static android.provider.Settings.EXTRA_ENABLE_MMS_DATA_REQUEST_REASON;
 import static android.provider.Settings.EXTRA_SUB_ID;
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
 import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
@@ -209,6 +210,7 @@
                 .setContentText(resources.getText(R.string.sim_notification_summary))
                 .setAutoCancel(true);
         Intent resultIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+        resultIntent.setPackage(SETTINGS_PACKAGE_NAME);
         resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
                 PendingIntent.FLAG_CANCEL_CURRENT);
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index a5768d3..bb7def6 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -217,6 +217,16 @@
             .build();
 
     /**
+     * Full {@link Uri} for the all volume Slices.
+     */
+    public static final Uri VOLUME_SLICES_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+            .appendPath("volume_slices")
+            .build();
+
+    /**
      * Full {@link Uri} for the Wifi Calling Slice.
      */
     public static final Uri WIFI_CALLING_URI = new Uri.Builder()
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index c22d001..5a1c424 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -47,6 +47,7 @@
 import com.android.settings.Utils;
 import com.android.settings.bluetooth.BluetoothSliceBuilder;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.notification.VolumeSeekBarPreferenceController;
 import com.android.settings.notification.zen.ZenModeSliceBuilder;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.SliceBroadcastRelay;
@@ -184,7 +185,10 @@
 
     @Override
     public void onSliceUnpinned(Uri sliceUri) {
-        SliceBroadcastRelay.unregisterReceivers(getContext(), sliceUri);
+        final Context context = getContext();
+        if (!VolumeSliceHelper.unregisterUri(context, sliceUri)) {
+            SliceBroadcastRelay.unregisterReceivers(context, sliceUri);
+        }
         ThreadUtils.postOnMainThread(() -> stopBackgroundWorker(sliceUri));
     }
 
@@ -390,7 +394,13 @@
 
         final IntentFilter filter = controller.getIntentFilter();
         if (filter != null) {
-            registerIntentToUri(filter, uri);
+            if (controller instanceof VolumeSeekBarPreferenceController) {
+                // Register volume slices to a broadcast relay to reduce unnecessary UI updates
+                VolumeSliceHelper.registerIntentToUri(getContext(), filter, uri,
+                        ((VolumeSeekBarPreferenceController) controller).getAudioStream());
+            } else {
+                registerIntentToUri(filter, uri);
+            }
         }
 
         ThreadUtils.postOnMainThread(() -> startBackgroundWorker(controller, uri));
diff --git a/src/com/android/settings/slices/VolumeSliceHelper.java b/src/com/android/settings/slices/VolumeSliceHelper.java
new file mode 100644
index 0000000..bcf02e5
--- /dev/null
+++ b/src/com/android/settings/slices/VolumeSliceHelper.java
@@ -0,0 +1,127 @@
+/*
+ * 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.slices;
+
+import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SLICES_URI;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.SliceBroadcastRelay;
+
+import java.util.Map;
+
+/**
+ * This helper is to handle the broadcasts of volume slices
+ */
+public class VolumeSliceHelper {
+
+    private static final String TAG = "VolumeSliceHelper";
+
+    @VisibleForTesting
+    static Map<Uri, Integer> sRegisteredUri = new ArrayMap<>();
+    @VisibleForTesting
+    static IntentFilter sIntentFilter;
+
+    static void registerIntentToUri(Context context, IntentFilter intentFilter, Uri sliceUri,
+            int audioStream) {
+        Log.d(TAG, "Registering uri for broadcast relay: " + sliceUri);
+        synchronized (sRegisteredUri) {
+            if (sRegisteredUri.isEmpty()) {
+                SliceBroadcastRelay.registerReceiver(context, VOLUME_SLICES_URI,
+                        VolumeSliceRelayReceiver.class, intentFilter);
+                sIntentFilter = intentFilter;
+            }
+            sRegisteredUri.put(sliceUri, audioStream);
+        }
+    }
+
+    static boolean unregisterUri(Context context, Uri sliceUri) {
+        if (!sRegisteredUri.containsKey(sliceUri)) {
+            return false;
+        }
+
+        Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri);
+        synchronized (sRegisteredUri) {
+            sRegisteredUri.remove(sliceUri);
+            if (sRegisteredUri.isEmpty()) {
+                sIntentFilter = null;
+                SliceBroadcastRelay.unregisterReceivers(context, VOLUME_SLICES_URI);
+            }
+        }
+        return true;
+    }
+
+    static void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (sIntentFilter == null || action == null || !sIntentFilter.hasAction(action)) {
+            return;
+        }
+
+        final String uriString = intent.getStringExtra(SliceBroadcastRelay.EXTRA_URI);
+        if (uriString == null) {
+            return;
+        }
+
+        final Uri uri = Uri.parse(uriString);
+        if (!VOLUME_SLICES_URI.equals(ContentProvider.getUriWithoutUserId(uri))) {
+            Log.w(TAG, "Invalid uri: " + uriString);
+            return;
+        }
+
+        if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
+            handleVolumeChanged(context, intent);
+        } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(action)
+                || AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
+            handleStreamChanged(context, intent);
+        } else {
+            notifyAllStreamsChanged(context);
+        }
+    }
+
+    private static void handleVolumeChanged(Context context, Intent intent) {
+        final int vol = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+        final int prevVol = intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
+        if (vol != prevVol) {
+            handleStreamChanged(context, intent);
+        }
+    }
+
+    private static void handleStreamChanged(Context context, Intent intent) {
+        final int inputType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+        for (Map.Entry<Uri, Integer> entry : sRegisteredUri.entrySet()) {
+            if (entry.getValue() == inputType) {
+                context.getContentResolver().notifyChange(entry.getKey(), null /* observer */);
+                break;
+            }
+        }
+    }
+
+    private static void notifyAllStreamsChanged(Context context) {
+        sRegisteredUri.forEach((uri, audioStream) -> {
+            context.getContentResolver().notifyChange(uri, null /* observer */);
+        });
+    }
+}
diff --git a/src/com/android/settings/slices/VolumeSliceRelayReceiver.java b/src/com/android/settings/slices/VolumeSliceRelayReceiver.java
new file mode 100644
index 0000000..f6088d0
--- /dev/null
+++ b/src/com/android/settings/slices/VolumeSliceRelayReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * 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.slices;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receives broadcasts to notify that Settings volume Slices are potentially stale.
+ */
+public class VolumeSliceRelayReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        VolumeSliceHelper.onReceive(context, intent);
+    }
+}
diff --git a/src/com/android/settings/sound/MediaControlsPreferenceController.java b/src/com/android/settings/sound/MediaControlsPreferenceController.java
index 050cf93..219eb24 100644
--- a/src/com/android/settings/sound/MediaControlsPreferenceController.java
+++ b/src/com/android/settings/sound/MediaControlsPreferenceController.java
@@ -35,12 +35,12 @@
     @Override
     public boolean isChecked() {
         int val = Settings.Secure.getInt(mContext.getContentResolver(), MEDIA_CONTROLS_RESUME, 1);
-        return val == 0;
+        return val == 1;
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        int val = isChecked ? 0 : 1;
+        int val = isChecked ? 1 : 0;
         return Settings.Secure.putInt(mContext.getContentResolver(), MEDIA_CONTROLS_RESUME, val);
     }
 
diff --git a/src/com/android/settings/widget/MasterSwitchPreference.java b/src/com/android/settings/widget/MasterSwitchPreference.java
index 9fe077e..7221035 100644
--- a/src/com/android/settings/widget/MasterSwitchPreference.java
+++ b/src/com/android/settings/widget/MasterSwitchPreference.java
@@ -23,6 +23,8 @@
 import android.view.View.OnClickListener;
 import android.widget.Switch;
 
+import androidx.annotation.Keep;
+import androidx.annotation.Nullable;
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
@@ -101,6 +103,16 @@
         return mSwitch != null && mChecked;
     }
 
+    /**
+     * Used to validate the state of mChecked and mCheckedSet when testing, without requiring
+     * that a ViewHolder be bound to the object.
+     */
+    @Keep
+    @Nullable
+    public Boolean getCheckedState() {
+        return mCheckedSet ? mChecked : null;
+    }
+
     public void setChecked(boolean checked) {
         // Always set checked the first time; don't assume the field's default of false.
         final boolean changed = mChecked != checked;
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 1579188..07d270d 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -37,6 +37,8 @@
 import android.os.UserManager;
 import android.security.Credentials;
 import android.security.KeyStore;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.SpannableString;
@@ -80,7 +82,9 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -159,11 +163,11 @@
     private String mMultipleCertSetString;
     private String mUseSystemCertsString;
     private String mDoNotProvideEapUserCertString;
-    private String mDoNotValidateEapServerString;
 
     private ScrollView mDialogContainer;
     private Spinner mSecuritySpinner;
-    private Spinner mEapMethodSpinner;
+    @VisibleForTesting Spinner mEapMethodSpinner;
+    @VisibleForTesting Spinner mEapSimSpinner;    // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME.
     private Spinner mEapCaCertSpinner;
     private Spinner mEapOcspSpinner;
     private TextView mEapDomainView;
@@ -209,6 +213,8 @@
 
     private final WifiManager mWifiManager;
 
+    private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>();
+
     public WifiConfigController(WifiConfigUiBase parent, View view, AccessPoint accessPoint,
             int mode) {
         this (parent, view, accessPoint, mode, true /* requestFocus */);
@@ -264,8 +270,6 @@
         mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
         mDoNotProvideEapUserCertString =
             mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
-        mDoNotValidateEapServerString =
-            mContext.getString(R.string.wifi_do_not_validate_eap_server);
 
         mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button);
         mDialogContainer = mView.findViewById(R.id.dialog_scrollview);
@@ -554,12 +558,10 @@
                 // Disallow submit if the user has not selected a CA certificate for an EAP network
                 // configuration.
                 enabled = false;
-            }
-            if (caCertSelection.equals(mUseSystemCertsString)
-                    && mEapDomainView != null
+            } else if (mEapDomainView != null
                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
-                // Disallow submit if the user chooses to use system certificates for EAP server
+                // Disallow submit if the user chooses to use a certificate for EAP server
                 // validation, but does not provide a domain.
                 enabled = false;
             }
@@ -577,7 +579,6 @@
     }
 
     void showWarningMessagesIfAppropriate() {
-        mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE);
@@ -590,19 +591,11 @@
         }
         if (mEapCaCertSpinner != null
                 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
-            String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
-            if (caCertSelection.equals(mDoNotValidateEapServerString)) {
-                // Display warning if user chooses not to validate the EAP server with a
-                // user-supplied CA certificate in an EAP network configuration.
-                mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
-            }
-            if (caCertSelection.equals(mUseSystemCertsString)
-                    && mEapDomainView != null
+            if (mEapDomainView != null
                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
-                // Display warning if user chooses to use pre-installed public CA certificates
-                // without restricting the server domain that these certificates can be used to
-                // validate.
+                // Display warning if user chooses to use a certificate without restricting the
+                // server domain that these certificates can be used to validate.
                 mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
             }
         }
@@ -736,8 +729,7 @@
                 config.enterpriseConfig.setCaCertificateAliases(null);
                 config.enterpriseConfig.setCaPath(null);
                 config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
-                if (caCert.equals(mUnspecifiedCertString)
-                        || caCert.equals(mDoNotValidateEapServerString)) {
+                if (caCert.equals(mUnspecifiedCertString)) {
                     // ca_cert already set to null, so do nothing.
                 } else if (caCert.equals(mUseSystemCertsString)) {
                     config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
@@ -771,8 +763,7 @@
                 }
 
                 // Only set OCSP option if there is a valid CA certificate.
-                if (caCert.equals(mUnspecifiedCertString)
-                        || caCert.equals(mDoNotValidateEapServerString)) {
+                if (caCert.equals(mUnspecifiedCertString)) {
                     config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
                 } else {
                     config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition());
@@ -825,6 +816,12 @@
                 return null;
         }
 
+        if (config.enterpriseConfig.isAuthenticationSimBased()
+                && mActiveSubscriptionInfos.size() > 0) {
+            config.carrierId = mActiveSubscriptionInfos
+                    .get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
+        }
+
         config.setIpConfiguration(
                 new IpConfiguration(mIpAssignment, mProxySettings,
                                     mStaticIpConfiguration, mHttpProxy));
@@ -1013,6 +1010,7 @@
             initiateEnterpriseNetworkUi = true;
             mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
             mEapMethodSpinner.setOnItemSelectedListener(this);
+            mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim);
             mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
             mPhase2Spinner.setOnItemSelectedListener(this);
             mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
@@ -1049,18 +1047,20 @@
         }
 
         if (refreshCertificates) {
+            loadSims();
+
             loadCertificates(
                     mEapCaCertSpinner,
                     Credentials.CA_CERTIFICATE,
-                    mDoNotValidateEapServerString,
-                    false,
-                    true);
+                    null /* noCertificateString */,
+                    false /* showMultipleCerts */,
+                    true /* showUsePreinstalledCertOption */);
             loadCertificates(
                     mEapUserCertSpinner,
                     Credentials.USER_PRIVATE_KEY,
                     mDoNotProvideEapUserCertString,
-                    false,
-                    false);
+                    false /* showMultipleCerts */,
+                    false /* showUsePreinstalledCertOption */);
             // To avoid the user connects to a non-secure network unexpectedly,
             // request using system trusted certificates by default
             // unless the user explicitly chooses "Do not validate" or other
@@ -1070,9 +1070,10 @@
 
         // Modifying an existing network
         if (initiateEnterpriseNetworkUi && mAccessPoint != null && mAccessPoint.isSaved()) {
-            WifiEnterpriseConfig enterpriseConfig = mAccessPoint.getConfig().enterpriseConfig;
-            int eapMethod = enterpriseConfig.getEapMethod();
-            int phase2Method = enterpriseConfig.getPhase2Method();
+            final WifiConfiguration wifiConfig = mAccessPoint.getConfig();
+            final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
+            final int eapMethod = enterpriseConfig.getEapMethod();
+            final int phase2Method = enterpriseConfig.getPhase2Method();
             mEapMethodSpinner.setSelection(eapMethod);
             showEapFieldsByMethod(eapMethod);
             switch (eapMethod) {
@@ -1120,12 +1121,22 @@
                 default:
                     break;
             }
+
+            if (enterpriseConfig.isAuthenticationSimBased()) {
+                for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) {
+                    if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) {
+                        mEapSimSpinner.setSelection(i);
+                        break;
+                    }
+                }
+            }
+
             if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
                 setSelection(mEapCaCertSpinner, mUseSystemCertsString);
             } else {
                 String[] caCerts = enterpriseConfig.getCaCertificateAliases();
                 if (caCerts == null) {
-                    setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
+                    setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
                 } else if (caCerts.length == 1) {
                     setSelection(mEapCaCertSpinner, caCerts[0]);
                 } else {
@@ -1133,9 +1144,9 @@
                     loadCertificates(
                             mEapCaCertSpinner,
                             Credentials.CA_CERTIFICATE,
-                            mDoNotValidateEapServerString,
-                            true,
-                            true);
+                            null /* noCertificateString */,
+                            true /* showMultipleCerts */,
+                            true /* showUsePreinstalledCertOption */);
                     setSelection(mEapCaCertSpinner, mMultipleCertSetString);
                 }
             }
@@ -1208,6 +1219,7 @@
         mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE);
         mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
         mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
+        mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
 
         Context context = mConfigUi.getContext();
         switch (eapMethod) {
@@ -1218,12 +1230,14 @@
                 setDomainInvisible();
                 setAnonymousIdentInvisible();
                 setUserCertInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_TLS:
                 mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
                 setPhase2Invisible();
                 setAnonymousIdentInvisible();
                 setPasswordInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_PEAP:
                 // Reset adapter if needed
@@ -1245,6 +1259,7 @@
                 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
                 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
                 setUserCertInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_SIM:
             case WIFI_EAP_METHOD_AKA:
@@ -1262,8 +1277,7 @@
 
         if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
             String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
-            if (eapCertSelection.equals(mDoNotValidateEapServerString)
-                    || eapCertSelection.equals(mUnspecifiedCertString)) {
+            if (eapCertSelection.equals(mUnspecifiedCertString)) {
                 // Domain suffix matching is not relevant if the user hasn't chosen a CA
                 // certificate yet, or chooses not to validate the EAP server.
                 setDomainInvisible();
@@ -1282,11 +1296,13 @@
             mEapIdentityView.setText("");
             mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
             setPasswordInvisible();
+            mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
         } else {
             mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
+            mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
         }
     }
 
@@ -1450,6 +1466,44 @@
     }
 
     @VisibleForTesting
+    void loadSims() {
+        List<SubscriptionInfo> activeSubscriptionInfos = mContext
+                .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
+        if (activeSubscriptionInfos == null) {
+            activeSubscriptionInfos = Collections.EMPTY_LIST;
+        }
+        mActiveSubscriptionInfos.clear();
+
+        // De-duplicates active subscriptions and caches in mActiveSubscriptionInfos.
+        for (SubscriptionInfo newInfo : activeSubscriptionInfos) {
+            for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) {
+                if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) {
+                    continue;
+                }
+            }
+            mActiveSubscriptionInfos.add(newInfo);
+        }
+
+        // Shows disabled 'No SIM' when there is no active subscription.
+        if (mActiveSubscriptionInfos.size() == 0) {
+            final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)};
+            mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim));
+            mEapSimSpinner.setSelection(0 /* position */);
+            mEapSimSpinner.setEnabled(false);
+            return;
+        }
+
+        // Shows display name of each active subscription.
+        final String[] displayNames = mActiveSubscriptionInfos.stream().map(
+                SubscriptionInfo::getDisplayName).toArray(String[]::new);
+        mEapSimSpinner.setAdapter(getSpinnerAdapter(displayNames));
+        mEapSimSpinner.setSelection(0 /* position */);
+        if (displayNames.length == 1) {
+            mEapSimSpinner.setEnabled(false);
+        }
+    }
+
+    @VisibleForTesting
     void loadCertificates(
             Spinner spinner,
             String prefix,
@@ -1485,7 +1539,8 @@
                     }).collect(Collectors.toList()));
         }
 
-        if (mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
+        if (!TextUtils.isEmpty(noCertificateString)
+                && mAccessPointSecurity != AccessPoint.SECURITY_EAP_SUITE_B) {
             certs.add(noCertificateString);
         }
 
@@ -1713,7 +1768,8 @@
                 mContext.getResources().getStringArray(contentStringArrayResId));
     }
 
-    private ArrayAdapter<CharSequence> getSpinnerAdapter(
+    @VisibleForTesting
+    ArrayAdapter<CharSequence> getSpinnerAdapter(
             String[] contentStringArray) {
         ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
                 android.R.layout.simple_spinner_item, contentStringArray);
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 99907f3..7ae6061 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -35,6 +35,8 @@
 import android.os.UserManager;
 import android.security.Credentials;
 import android.security.KeyStore;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.SpannableString;
@@ -80,7 +82,9 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 import java.util.stream.Collectors;
 
 /**
@@ -156,11 +160,11 @@
     private String mMultipleCertSetString;
     private String mUseSystemCertsString;
     private String mDoNotProvideEapUserCertString;
-    private String mDoNotValidateEapServerString;
 
     private ScrollView mDialogContainer;
     private Spinner mSecuritySpinner;
-    private Spinner mEapMethodSpinner;
+    @VisibleForTesting Spinner mEapMethodSpinner;
+    @VisibleForTesting Spinner mEapSimSpinner;    // For EAP-SIM, EAP-AKA and EAP-AKA-PRIME.
     private Spinner mEapCaCertSpinner;
     private Spinner mEapOcspSpinner;
     private TextView mEapDomainView;
@@ -205,6 +209,8 @@
 
     private final WifiManager mWifiManager;
 
+    private final List<SubscriptionInfo> mActiveSubscriptionInfos = new ArrayList<>();
+
     public WifiConfigController2(WifiConfigUiBase2 parent, View view, WifiEntry wifiEntry,
             int mode) {
         mConfigUi = parent;
@@ -253,8 +259,6 @@
         mUseSystemCertsString = mContext.getString(R.string.wifi_use_system_certs);
         mDoNotProvideEapUserCertString =
             mContext.getString(R.string.wifi_do_not_provide_eap_user_cert);
-        mDoNotValidateEapServerString =
-            mContext.getString(R.string.wifi_do_not_validate_eap_server);
 
         mSsidScanButton = (ImageButton) mView.findViewById(R.id.ssid_scanner_button);
         mDialogContainer = mView.findViewById(R.id.dialog_scrollview);
@@ -533,12 +537,10 @@
                 // Disallow submit if the user has not selected a CA certificate for an EAP network
                 // configuration.
                 enabled = false;
-            }
-            if (caCertSelection.equals(mUseSystemCertsString)
-                    && mEapDomainView != null
+            } else if (mEapDomainView != null
                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
-                // Disallow submit if the user chooses to use system certificates for EAP server
+                // Disallow submit if the user chooses to use a certificate for EAP server
                 // validation, but does not provide a domain.
                 enabled = false;
             }
@@ -556,7 +558,6 @@
     }
 
     void showWarningMessagesIfAppropriate() {
-        mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.no_user_cert_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.no_domain_warning).setVisibility(View.GONE);
         mView.findViewById(R.id.ssid_too_long_warning).setVisibility(View.GONE);
@@ -569,19 +570,11 @@
         }
         if (mEapCaCertSpinner != null
                 && mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
-            String caCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
-            if (caCertSelection.equals(mDoNotValidateEapServerString)) {
-                // Display warning if user chooses not to validate the EAP server with a
-                // user-supplied CA certificate in an EAP network configuration.
-                mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
-            }
-            if (caCertSelection.equals(mUseSystemCertsString)
-                    && mEapDomainView != null
+            if (mEapDomainView != null
                     && mView.findViewById(R.id.l_domain).getVisibility() != View.GONE
                     && TextUtils.isEmpty(mEapDomainView.getText().toString())) {
-                // Display warning if user chooses to use pre-installed public CA certificates
-                // without restricting the server domain that these certificates can be used to
-                // validate.
+                // Display warning if user chooses to use a certificate without restricting the
+                // server domain that these certificates can be used to validate.
                 mView.findViewById(R.id.no_domain_warning).setVisibility(View.VISIBLE);
             }
         }
@@ -714,12 +707,17 @@
                         break;
                 }
 
+                if (config.enterpriseConfig.isAuthenticationSimBased()
+                        && mActiveSubscriptionInfos.size() > 0) {
+                    config.carrierId = mActiveSubscriptionInfos
+                            .get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
+                }
+
                 String caCert = (String) mEapCaCertSpinner.getSelectedItem();
                 config.enterpriseConfig.setCaCertificateAliases(null);
                 config.enterpriseConfig.setCaPath(null);
                 config.enterpriseConfig.setDomainSuffixMatch(mEapDomainView.getText().toString());
-                if (caCert.equals(mUnspecifiedCertString)
-                        || caCert.equals(mDoNotValidateEapServerString)) {
+                if (caCert.equals(mUnspecifiedCertString)) {
                     // ca_cert already set to null, so do nothing.
                 } else if (caCert.equals(mUseSystemCertsString)) {
                     config.enterpriseConfig.setCaPath(SYSTEM_CA_STORE_PATH);
@@ -752,8 +750,7 @@
                 }
 
                 // Only set OCSP option if there is a valid CA certificate.
-                if (caCert.equals(mUnspecifiedCertString)
-                        || caCert.equals(mDoNotValidateEapServerString)) {
+                if (caCert.equals(mUnspecifiedCertString)) {
                     config.enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
                 } else {
                     config.enterpriseConfig.setOcsp(mEapOcspSpinner.getSelectedItemPosition());
@@ -994,6 +991,7 @@
             initiateEnterpriseNetworkUi = true;
             mEapMethodSpinner = (Spinner) mView.findViewById(R.id.method);
             mEapMethodSpinner.setOnItemSelectedListener(this);
+            mEapSimSpinner = (Spinner) mView.findViewById(R.id.sim);
             mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
             mPhase2Spinner.setOnItemSelectedListener(this);
             mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
@@ -1031,18 +1029,20 @@
         }
 
         if (refreshCertificates) {
+            loadSims();
+
             loadCertificates(
                     mEapCaCertSpinner,
                     Credentials.CA_CERTIFICATE,
-                    mDoNotValidateEapServerString,
-                    false,
-                    true);
+                    null /* noCertificateString */,
+                    false /* showMultipleCerts */,
+                    true /* showUsePreinstalledCertOption */);
             loadCertificates(
                     mEapUserCertSpinner,
                     Credentials.USER_PRIVATE_KEY,
                     mDoNotProvideEapUserCertString,
-                    false,
-                    false);
+                    false /* showMultipleCerts */,
+                    false /* showUsePreinstalledCertOption */);
             // To avoid the user connects to a non-secure network unexpectedly,
             // request using system trusted certificates by default
             // unless the user explicitly chooses "Do not validate" or other
@@ -1052,10 +1052,10 @@
 
         // Modifying an existing network
         if (initiateEnterpriseNetworkUi && mWifiEntry != null && mWifiEntry.isSaved()) {
-            WifiEnterpriseConfig enterpriseConfig = mWifiEntry.getWifiConfiguration()
-                    .enterpriseConfig;
-            int eapMethod = enterpriseConfig.getEapMethod();
-            int phase2Method = enterpriseConfig.getPhase2Method();
+            final WifiConfiguration wifiConfig = mWifiEntry.getWifiConfiguration();
+            final WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
+            final int eapMethod = enterpriseConfig.getEapMethod();
+            final int phase2Method = enterpriseConfig.getPhase2Method();
             mEapMethodSpinner.setSelection(eapMethod);
             showEapFieldsByMethod(eapMethod);
             switch (eapMethod) {
@@ -1103,12 +1103,22 @@
                 default:
                     break;
             }
+
+            if (enterpriseConfig.isAuthenticationSimBased()) {
+                for (int i = 0; i < mActiveSubscriptionInfos.size(); i++) {
+                    if (wifiConfig.carrierId == mActiveSubscriptionInfos.get(i).getCarrierId()) {
+                        mEapSimSpinner.setSelection(i);
+                        break;
+                    }
+                }
+            }
+
             if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
                 setSelection(mEapCaCertSpinner, mUseSystemCertsString);
             } else {
                 String[] caCerts = enterpriseConfig.getCaCertificateAliases();
                 if (caCerts == null) {
-                    setSelection(mEapCaCertSpinner, mDoNotValidateEapServerString);
+                    setSelection(mEapCaCertSpinner, mUnspecifiedCertString);
                 } else if (caCerts.length == 1) {
                     setSelection(mEapCaCertSpinner, caCerts[0]);
                 } else {
@@ -1116,9 +1126,9 @@
                     loadCertificates(
                             mEapCaCertSpinner,
                             Credentials.CA_CERTIFICATE,
-                            mDoNotValidateEapServerString,
-                            true,
-                            true);
+                            null /* noCertificateString */,
+                            true /* showMultipleCerts */,
+                            true /* showUsePreinstalledCertOption */);
                     setSelection(mEapCaCertSpinner, mMultipleCertSetString);
                 }
             }
@@ -1191,6 +1201,7 @@
         mView.findViewById(R.id.l_ocsp).setVisibility(View.VISIBLE);
         mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
         mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
+        mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
 
         Context context = mConfigUi.getContext();
         switch (eapMethod) {
@@ -1201,12 +1212,14 @@
                 setDomainInvisible();
                 setAnonymousIdentInvisible();
                 setUserCertInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_TLS:
                 mView.findViewById(R.id.l_user_cert).setVisibility(View.VISIBLE);
                 setPhase2Invisible();
                 setAnonymousIdentInvisible();
                 setPasswordInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_PEAP:
                 // Reset adapter if needed
@@ -1228,6 +1241,7 @@
                 mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
                 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
                 setUserCertInvisible();
+                mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
                 break;
             case WIFI_EAP_METHOD_SIM:
             case WIFI_EAP_METHOD_AKA:
@@ -1245,8 +1259,7 @@
 
         if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
             String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
-            if (eapCertSelection.equals(mDoNotValidateEapServerString)
-                    || eapCertSelection.equals(mUnspecifiedCertString)) {
+            if (eapCertSelection.equals(mUnspecifiedCertString)) {
                 // Domain suffix matching is not relevant if the user hasn't chosen a CA
                 // certificate yet, or chooses not to validate the EAP server.
                 setDomainInvisible();
@@ -1265,11 +1278,13 @@
             mEapIdentityView.setText("");
             mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
             setPasswordInvisible();
+            mView.findViewById(R.id.l_sim).setVisibility(View.VISIBLE);
         } else {
             mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.password_layout).setVisibility(View.VISIBLE);
             mView.findViewById(R.id.show_password_layout).setVisibility(View.VISIBLE);
+            mView.findViewById(R.id.l_sim).setVisibility(View.GONE);
         }
     }
 
@@ -1433,6 +1448,44 @@
     }
 
     @VisibleForTesting
+    void loadSims() {
+        List<SubscriptionInfo> activeSubscriptionInfos = mContext
+                .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
+        if (activeSubscriptionInfos == null) {
+            activeSubscriptionInfos = Collections.EMPTY_LIST;
+        }
+        mActiveSubscriptionInfos.clear();
+
+        // De-duplicates active subscriptions and caches in mActiveSubscriptionInfos.
+        for (SubscriptionInfo newInfo : activeSubscriptionInfos) {
+            for (SubscriptionInfo cachedInfo : mActiveSubscriptionInfos) {
+                if (newInfo.getCarrierId() == cachedInfo.getCarrierId()) {
+                    continue;
+                }
+            }
+            mActiveSubscriptionInfos.add(newInfo);
+        }
+
+        // Shows disabled 'No SIM' when there is no active subscription.
+        if (mActiveSubscriptionInfos.size() == 0) {
+            final String[] noSim = new String[]{mContext.getString(R.string.wifi_no_sim_card)};
+            mEapSimSpinner.setAdapter(getSpinnerAdapter(noSim));
+            mEapSimSpinner.setSelection(0 /* position */);
+            mEapSimSpinner.setEnabled(false);
+            return;
+        }
+
+        // Shows display name of each active subscription.
+        final String[] displayNames = mActiveSubscriptionInfos.stream().map(
+                SubscriptionInfo::getDisplayName).toArray(String[]::new);
+        mEapSimSpinner.setAdapter(getSpinnerAdapter(displayNames));
+        mEapSimSpinner.setSelection(0 /* position */);
+        if (displayNames.length == 1) {
+            mEapSimSpinner.setEnabled(false);
+        }
+    }
+
+    @VisibleForTesting
     void loadCertificates(
             Spinner spinner,
             String prefix,
@@ -1468,7 +1521,8 @@
                     }).collect(Collectors.toList()));
         }
 
-        if (mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) {
+        if (!TextUtils.isEmpty(noCertificateString)
+                && mWifiEntrySecurity != WifiEntry.SECURITY_EAP_SUITE_B) {
             certs.add(noCertificateString);
         }
 
@@ -1696,7 +1750,8 @@
                 mContext.getResources().getStringArray(contentStringArrayResId));
     }
 
-    private ArrayAdapter<CharSequence> getSpinnerAdapter(
+    @VisibleForTesting
+    ArrayAdapter<CharSequence> getSpinnerAdapter(
             String[] contentStringArray) {
         ArrayAdapter<CharSequence> spinnerAdapter = new ArrayAdapter<>(mContext,
                 android.R.layout.simple_spinner_item, contentStringArray);
diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
index 742edd1..126a72e 100644
--- a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
@@ -17,32 +17,20 @@
 package com.android.settings.wifi;
 
 import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkScoreManager;
-import android.net.wifi.WifiManager;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.SimpleClock;
-import android.os.SystemClock;
 
-import androidx.annotation.VisibleForTesting;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
+import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.wifi.WifiEntryPreference;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiPickerTracker;
-
-import java.time.Clock;
-import java.time.ZoneOffset;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 // TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController.
 /**
@@ -50,28 +38,21 @@
  * controller class when there is a wifi connection present.
  */
 public class WifiConnectionPreferenceController extends AbstractPreferenceController implements
-        WifiPickerTracker.WifiPickerTrackerCallback {
+        WifiTracker.WifiListener {
 
     private static final String TAG = "WifiConnPrefCtrl";
 
     private static final String KEY = "active_wifi_connection";
 
-    // Max age of tracked WifiEntries.
-    private static final long MAX_SCAN_AGE_MILLIS = 15_000;
-    // Interval between initiating WifiPickerTracker scans.
-    private static final long SCAN_INTERVAL_MILLIS = 10_000;
-
     private UpdateListener mUpdateListener;
     private Context mPrefContext;
     private String mPreferenceGroupKey;
     private PreferenceGroup mPreferenceGroup;
-    @VisibleForTesting
-    public WifiPickerTracker mWifiPickerTracker;
-    private WifiEntryPreference mPreference;
+    private WifiTracker mWifiTracker;
+    private AccessPointPreference mPreference;
+    private AccessPointPreference.UserBadgeCache mBadgeCache;
     private int order;
     private int mMetricsCategory;
-    // Worker thread used for WifiPickerTracker work.
-    private HandlerThread mWorkerThread;
 
     /**
      * Used to notify a parent controller that this controller has changed in availability, or has
@@ -99,34 +80,16 @@
         super(context);
         mUpdateListener = updateListener;
         mPreferenceGroupKey = preferenceGroupKey;
+        mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */,
+                true /* includeScans */);
         this.order = order;
         mMetricsCategory = metricsCategory;
-
-        mWorkerThread = new HandlerThread(
-                TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        mWorkerThread.start();
-        final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
-            @Override
-            public long millis() {
-                return SystemClock.elapsedRealtime();
-            }
-        };
-        mWifiPickerTracker = new WifiPickerTracker(lifecycle, context,
-                context.getSystemService(WifiManager.class),
-                context.getSystemService(ConnectivityManager.class),
-                context.getSystemService(NetworkScoreManager.class),
-                new Handler(Looper.getMainLooper()),
-                mWorkerThread.getThreadHandler(),
-                elapsedRealtimeClock,
-                MAX_SCAN_AGE_MILLIS,
-                SCAN_INTERVAL_MILLIS,
-                this);
+        mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager());
     }
 
     @Override
     public boolean isAvailable() {
-        return mWifiPickerTracker.getConnectedWifiEntry() != null;
+        return mWifiTracker.isConnected() && getCurrentAccessPoint() != null;
     }
 
     @Override
@@ -142,69 +105,74 @@
         update();
     }
 
-    private void updatePreference(WifiEntry wifiEntry) {
+    private AccessPoint getCurrentAccessPoint() {
+        for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) {
+            if (accessPoint.isActive()) {
+                return accessPoint;
+            }
+        }
+        return null;
+    }
+
+    private void updatePreference(AccessPoint accessPoint) {
         if (mPreference != null) {
             mPreferenceGroup.removePreference(mPreference);
             mPreference = null;
         }
-        if (wifiEntry == null || mPrefContext == null) {
+        if (accessPoint == null) {
             return;
         }
+        if (mPrefContext != null) {
+            mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache,
+                    R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */);
+            mPreference.setKey(KEY);
+            mPreference.refresh();
+            mPreference.setOrder(order);
 
-        mPreference = new WifiEntryPreference(mPrefContext, wifiEntry);
-        mPreference.setKey(KEY);
-        mPreference.refresh();
-        mPreference.setOrder(order);
-        mPreference.setOnPreferenceClickListener(pref -> {
-            final Bundle args = new Bundle();
-            args.putString(WifiNetworkDetailsFragment2.KEY_CHOSEN_WIFIENTRY_KEY,
-                    wifiEntry.getKey());
-            new SubSettingLauncher(mPrefContext)
-                    .setTitleRes(R.string.pref_title_network_details)
-                    .setDestination(WifiNetworkDetailsFragment2.class.getName())
-                    .setArguments(args)
-                    .setSourceMetricsCategory(mMetricsCategory)
-                    .launch();
-            return true;
-        });
-        mPreferenceGroup.addPreference(mPreference);
+            mPreference.setOnPreferenceClickListener(pref -> {
+                Bundle args = new Bundle();
+                mPreference.getAccessPoint().saveWifiState(args);
+                new SubSettingLauncher(mPrefContext)
+                        .setTitleRes(R.string.pref_title_network_details)
+                        .setDestination(WifiNetworkDetailsFragment.class.getName())
+                        .setArguments(args)
+                        .setSourceMetricsCategory(mMetricsCategory)
+                        .launch();
+                return true;
+            });
+            mPreferenceGroup.addPreference(mPreference);
+        }
     }
 
     private void update() {
-        final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry();
-        if (connectedWifiEntry == null) {
+        AccessPoint connectedAccessPoint = null;
+        if (mWifiTracker.isConnected()) {
+            connectedAccessPoint = getCurrentAccessPoint();
+        }
+        if (connectedAccessPoint == null) {
             updatePreference(null);
         } else {
-            if (mPreference == null || !mPreference.getWifiEntry().equals(connectedWifiEntry)) {
-                updatePreference(connectedWifiEntry);
-            } else if (mPreference != null) {
-                mPreference.refresh();
-            }
+          if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) {
+              updatePreference(connectedAccessPoint);
+          } else if (mPreference != null) {
+              mPreference.refresh();
+          }
         }
         mUpdateListener.onChildrenUpdated();
     }
 
-    /** Called when the state of Wifi has changed. */
     @Override
-    public void onWifiStateChanged() {
-        update();
-    }
-
-    /**
-     * Update the results when data changes.
-     */
-    @Override
-    public void onWifiEntriesChanged() {
+    public void onWifiStateChanged(int state) {
         update();
     }
 
     @Override
-    public void onNumSavedSubscriptionsChanged() {
-        // Do nothing.
+    public void onConnectedChanged() {
+        update();
     }
 
     @Override
-    public void onNumSavedNetworksChanged() {
-        // Do nothing.
+    public void onAccessPointsChanged() {
+        update();
     }
 }
diff --git a/src/com/android/settings/wifi/WifiUtils.java b/src/com/android/settings/wifi/WifiUtils.java
index 1333ab4..2214605 100644
--- a/src/com/android/settings/wifi/WifiUtils.java
+++ b/src/com/android/settings/wifi/WifiUtils.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager;
 import android.net.NetworkCapabilities;
 import android.net.wifi.ScanResult;
+import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -38,8 +39,6 @@
 
     private static final int SSID_ASCII_MIN_LENGTH = 1;
     private static final int SSID_ASCII_MAX_LENGTH = 32;
-    private static final int PASSWORD_MIN_LENGTH = 8;
-    private static final int PASSWORD_MAX_LENGTH = 63;
 
 
     public static boolean isSSIDTooLong(String ssid) {
@@ -56,13 +55,17 @@
         return ssid.length() < SSID_ASCII_MIN_LENGTH;
     }
 
-    public static boolean isHotspotPasswordValid(String password) {
-        if (TextUtils.isEmpty(password)) {
+    /**
+     * Check if the WPA2-PSK hotspot password is valid.
+     */
+    public static boolean isHotspotWpa2PasswordValid(String password) {
+        final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+        try {
+            configBuilder.setPassphrase(password, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        } catch (IllegalArgumentException e) {
             return false;
         }
-
-        final int length = password.length();
-        return length >= PASSWORD_MIN_LENGTH && length <= PASSWORD_MAX_LENGTH;
+        return true;
     }
 
     /**
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index 5c48dfd..7ef950f 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -23,8 +23,10 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.settings.SettingsEnums;
+import android.content.AsyncQueryHandler;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -41,9 +43,14 @@
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.Uri;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.provider.Telephony.CarrierId;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
 import android.util.Log;
@@ -98,6 +105,7 @@
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
+import java.util.List;
 import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
@@ -133,6 +141,8 @@
     @VisibleForTesting
     static final String KEY_SSID_PREF = "ssid";
     @VisibleForTesting
+    static final String KEY_EAP_SIM_SUBSCRIPTION_PREF = "eap_sim_subscription";
+    @VisibleForTesting
     static final String KEY_MAC_ADDRESS_PREF = "mac_address";
     @VisibleForTesting
     static final String KEY_IP_ADDRESS_PREF = "ip_address";
@@ -156,6 +166,7 @@
     private NetworkInfo mNetworkInfo;
     private NetworkCapabilities mNetworkCapabilities;
     private int mRssiSignalLevel = -1;
+    @VisibleForTesting boolean mShowX; // Shows the Wi-Fi signal icon of Pie+x when it's true.
     private String[] mSignalStr;
     private WifiInfo mWifiInfo;
     private final WifiManager mWifiManager;
@@ -170,6 +181,7 @@
     private Preference mFrequencyPref;
     private Preference mSecurityPref;
     private Preference mSsidPref;
+    private Preference mEapSimSubscriptionPref;
     private Preference mMacAddressPref;
     private Preference mIpAddressPref;
     private Preference mGatewayPref;
@@ -187,6 +199,35 @@
     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
             .clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
 
+    private CarrierIdAsyncQueryHandler mCarrierIdAsyncQueryHandler;
+    private static final int TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY = 1;
+    private static final int COLUMN_CARRIER_NAME = 0;
+
+    private class CarrierIdAsyncQueryHandler extends AsyncQueryHandler {
+
+        private CarrierIdAsyncQueryHandler(Context context) {
+            super(context.getContentResolver());
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            if (token == TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY) {
+                if (mContext == null || cursor == null || !cursor.moveToFirst()) {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                    mEapSimSubscriptionPref.setSummary(R.string.wifi_require_sim_card_to_connect);
+                    return;
+                }
+                mEapSimSubscriptionPref.setSummary(mContext.getString(
+                        R.string.wifi_require_specific_sim_card_to_connect,
+                        cursor.getString(COLUMN_CARRIER_NAME)));
+                cursor.close();
+                return;
+            }
+        }
+    }
+
     // Must be run on the UI thread since it directly manipulates UI state.
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
@@ -336,6 +377,7 @@
         mSecurityPref = screen.findPreference(KEY_SECURITY_PREF);
 
         mSsidPref = screen.findPreference(KEY_SSID_PREF);
+        mEapSimSubscriptionPref = screen.findPreference(KEY_EAP_SIM_SUBSCRIPTION_PREF);
         mMacAddressPref = screen.findPreference(KEY_MAC_ADDRESS_PREF);
         mIpAddressPref = screen.findPreference(KEY_IP_ADDRESS_PREF);
         mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF);
@@ -507,12 +549,14 @@
         refreshIpLayerInfo();
         // SSID Pref
         refreshSsid();
+        // EAP SIM subscription
+        refreshEapSimSubscription();
         // MAC Address Pref
         refreshMacAddress();
     }
 
     private void refreshRssiViews() {
-        int signalLevel = mWifiEntry.getLevel();
+        final int signalLevel = mWifiEntry.getLevel();
 
         // Disappears signal view if not in range. e.g. for saved networks.
         if (signalLevel == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
@@ -521,11 +565,14 @@
             return;
         }
 
-        if (mRssiSignalLevel == signalLevel) {
+        final boolean showX = mWifiEntry.shouldShowXLevelIcon();
+
+        if (mRssiSignalLevel == signalLevel && mShowX == showX) {
             return;
         }
         mRssiSignalLevel = signalLevel;
-        Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
+        mShowX = showX;
+        Drawable wifiIcon = mIconInjector.getIcon(mShowX, mRssiSignalLevel);
 
         if (mEntityHeaderController != null) {
             mEntityHeaderController
@@ -630,6 +677,62 @@
         }
     }
 
+    private void refreshEapSimSubscription() {
+        mEapSimSubscriptionPref.setVisible(false);
+
+        if (mWifiEntry.getSecurity() != WifiEntry.SECURITY_EAP) {
+            return;
+        }
+        final WifiConfiguration config = mWifiEntry.getWifiConfiguration();
+        if (config == null || config.enterpriseConfig == null) {
+            return;
+        }
+        if (!config.enterpriseConfig.isAuthenticationSimBased()) {
+            return;
+        }
+
+        mEapSimSubscriptionPref.setVisible(true);
+
+        // Checks if the SIM subscription is active.
+        final List<SubscriptionInfo> activeSubscriptionInfos = mContext
+                .getSystemService(SubscriptionManager.class).getActiveSubscriptionInfoList();
+        final int defaultDataSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
+        if (activeSubscriptionInfos != null) {
+            for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfos) {
+                if (config.carrierId == subscriptionInfo.getCarrierId()) {
+                    mEapSimSubscriptionPref.setSummary(subscriptionInfo.getDisplayName());
+                    return;
+                }
+
+                // When it's UNKNOWN_CARRIER_ID, devices connects it with the SIM subscription of
+                // defaultDataSubscriptionId.
+                if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID
+                        && defaultDataSubscriptionId == subscriptionInfo.getSubscriptionId()) {
+                    mEapSimSubscriptionPref.setSummary(subscriptionInfo.getDisplayName());
+                    return;
+                }
+            }
+        }
+
+        if (config.carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
+            mEapSimSubscriptionPref.setSummary(R.string.wifi_no_related_sim_card);
+            return;
+        }
+
+        // The Wi-Fi network has specified carrier id, query carrier name from CarrierIdProvider.
+        if (mCarrierIdAsyncQueryHandler == null) {
+            mCarrierIdAsyncQueryHandler = new CarrierIdAsyncQueryHandler(mContext);
+        }
+        mCarrierIdAsyncQueryHandler.cancelOperation(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY);
+        mCarrierIdAsyncQueryHandler.startQuery(TOKEN_QUERY_CARRIER_ID_AND_UPDATE_SIM_SUMMARY,
+                null /* cookie */,
+                CarrierId.All.CONTENT_URI,
+                new String[]{CarrierId.CARRIER_NAME},
+                CarrierId.CARRIER_ID + "=?",
+                new String[] {Integer.toString(config.carrierId)},
+                null /* orderBy */);
+    }
+
     private void refreshMacAddress() {
         final String macAddress = mWifiEntry.getMacAddress();
         if (TextUtils.isEmpty(macAddress)) {
@@ -907,8 +1010,8 @@
             mContext = context;
         }
 
-        public Drawable getIcon(int level) {
-            return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
+        public Drawable getIcon(boolean showX, int level) {
+            return mContext.getDrawable(Utils.getWifiIconResource(showX, level)).mutate();
         }
     }
 
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
index a4a51dc..be67d22 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
@@ -118,7 +118,7 @@
 
     @Override
     public boolean isTextValid(String value) {
-        return WifiUtils.isHotspotPasswordValid(value);
+        return WifiUtils.isHotspotWpa2PasswordValid(value);
     }
 
     private static String generateRandomPassword() {
diff --git a/tests/robotests/src/com/android/settings/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/TetherSettingsTest.java
index 3c38c51d..99c112c 100644
--- a/tests/robotests/src/com/android/settings/TetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/TetherSettingsTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -25,20 +27,27 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.FeatureFlagUtils;
 
+import androidx.fragment.app.FragmentActivity;
 import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
 
 import com.android.settings.core.FeatureFlags;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -160,6 +169,80 @@
         verify(mockPreference).setTitle(R.string.tethering_footer_info_sta_ap_concurrency);
     }
 
+    @Test
+    public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOn() {
+        final TetherSettings spyTetherSettings = spy(new TetherSettings());
+        when(spyTetherSettings.getContext()).thenReturn(mContext);
+        final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class);
+        when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING))
+            .thenReturn(mockSwitchPreference);
+        final FragmentActivity mockActivity = mock(FragmentActivity.class);
+        when(spyTetherSettings.getActivity()).thenReturn(mockActivity);
+        final ArgumentCaptor<BroadcastReceiver> captor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        when(mockActivity.registerReceiver(captor.capture(), any(IntentFilter.class)))
+            .thenReturn(null);
+        // Bluetooth tethering state is on
+        when(spyTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+        when(spyTetherSettings.isBluetoothTetheringOn()).thenReturn(true);
+
+        spyTetherSettings.setupTetherPreference();
+        spyTetherSettings.registerReceiver();
+        updateOnlyBluetoothState(spyTetherSettings);
+
+        // Simulate Bluetooth tethering state changed
+        final BroadcastReceiver receiver = captor.getValue();
+        final Intent bluetoothTetheringChanged =
+                new Intent(TetherSettings.BLUETOOTH_TETHERING_STATE_CHANGED);
+        receiver.onReceive(mockActivity, bluetoothTetheringChanged);
+
+        verify(mockSwitchPreference).setEnabled(true);
+        verify(mockSwitchPreference).setChecked(true);
+    }
+
+    @Test
+    public void testBluetoothState_updateBluetoothState_bluetoothTetheringStateOff() {
+        final TetherSettings spyTetherSettings = spy(new TetherSettings());
+        when(spyTetherSettings.getContext()).thenReturn(mContext);
+        final SwitchPreference mockSwitchPreference = mock(SwitchPreference.class);
+        when(spyTetherSettings.findPreference(TetherSettings.KEY_ENABLE_BLUETOOTH_TETHERING))
+            .thenReturn(mockSwitchPreference);
+        final FragmentActivity mockActivity = mock(FragmentActivity.class);
+        when(spyTetherSettings.getActivity()).thenReturn(mockActivity);
+        final ArgumentCaptor<BroadcastReceiver> captor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        when(mockActivity.registerReceiver(captor.capture(), any(IntentFilter.class)))
+            .thenReturn(null);
+        // Bluetooth tethering state is off
+        when(spyTetherSettings.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+        when(spyTetherSettings.isBluetoothTetheringOn()).thenReturn(false);
+
+        spyTetherSettings.setupTetherPreference();
+        spyTetherSettings.registerReceiver();
+        updateOnlyBluetoothState(spyTetherSettings);
+
+        // Simulate Bluetooth tethering state changed
+        final BroadcastReceiver receiver = captor.getValue();
+        final Intent bluetoothTetheringChanged =
+                new Intent(TetherSettings.BLUETOOTH_TETHERING_STATE_CHANGED);
+        receiver.onReceive(mockActivity, bluetoothTetheringChanged);
+
+        verify(mockSwitchPreference).setEnabled(true);
+        verify(mockSwitchPreference).setChecked(false);
+    }
+
+    private void updateOnlyBluetoothState(TetherSettings tetherSettings) {
+        doReturn(mConnectivityManager).when(tetherSettings)
+            .getSystemService(Context.CONNECTIVITY_SERVICE);
+        when(mConnectivityManager.getTetherableIfaces()).thenReturn(new String[0]);
+        when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[0]);
+        when(mConnectivityManager.getTetheringErroredIfaces()).thenReturn(new String[0]);
+        doNothing().when(tetherSettings).updateUsbState(any(String[].class), any(String[].class),
+                any(String[].class));
+        doNothing().when(tetherSettings).updateEthernetState(any(String[].class),
+                any(String[].class));
+    }
+
     private void setupIsTetherAvailable(boolean returnValue) {
         when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
 
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index 2aa8418..303fb1b 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -40,6 +40,8 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.VectorDrawable;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -299,4 +301,33 @@
 
         assertThat(Utils.isSettingsIntelligence(mContext)).isFalse();
     }
+
+    @Test
+    public void isMediaOutputDisabled_infosSizeEqual1_returnsTrue() {
+        final MediaRouter2Manager router2Manager = mock(MediaRouter2Manager.class);
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+
+        when(router2Manager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(0);
+
+        assertThat(Utils.isMediaOutputDisabled(router2Manager, "test")).isTrue();
+    }
+
+    @Test
+    public void isMediaOutputDisabled_infosSizeOverThan1_returnsFalse() {
+        final MediaRouter2Manager router2Manager = mock(MediaRouter2Manager.class);
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        infos.add(info2);
+
+        when(router2Manager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(0);
+        when(info2.getType()).thenReturn(0);
+
+        assertThat(Utils.isMediaOutputDisabled(router2Manager, "test")).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
index 4d420d1..a27e40d 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java
@@ -570,12 +570,14 @@
 
         mFragment.createHeader();
 
-        assertThat(mFragment.mFilterAdapter.getCount()).isEqualTo(3);
+        assertThat(mFragment.mFilterAdapter.getCount()).isEqualTo(4);
         assertThat(mFragment.mFilterAdapter.getItem(0)).isEqualTo(
                 mContext.getString(R.string.sort_order_recent_notification));
         assertThat(mFragment.mFilterAdapter.getItem(1)).isEqualTo(
                 mContext.getString(R.string.sort_order_frequent_notification));
         assertThat(mFragment.mFilterAdapter.getItem(2)).isEqualTo(
+                mContext.getString(R.string.filter_all_apps));
+        assertThat(mFragment.mFilterAdapter.getItem(3)).isEqualTo(
                 mContext.getString(R.string.filter_notif_blocked_apps));
     }
 
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
index 6041e9d..8febbc6 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
@@ -63,12 +63,6 @@
     }
 
     @Test
-    public void isAvailable_lowMemory_false() {
-        mActivityManager.setIsLowRamDevice(true);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
     public void logSpecialPermissionChange() {
         ZenAccessController.logSpecialPermissionChange(true, "app", mContext);
         verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
index cba1a51..fb565b6 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
@@ -65,7 +65,7 @@
     }
 
     @Test
-    public void onStart_lowMemory_shouldNotRegisterListener() {
+    public void onStart_shouldRegisterListener() {
         final ShadowActivityManager sam = Shadow.extract(
                 mContext.getSystemService(ActivityManager.class));
         sam.setIsLowRamDevice(true);
@@ -75,29 +75,11 @@
         mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
                 Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
 
-        verify(mListener, never()).onZenAccessPolicyChanged();
-    }
-
-    @Test
-    public void onStart_highMemory_shouldRegisterListener() {
-        final ShadowActivityManager sam = Shadow.extract(
-                mContext.getSystemService(ActivityManager.class));
-        sam.setIsLowRamDevice(false);
-
-        mLifecycle.handleLifecycleEvent(ON_START);
-
-        mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
-
         verify(mListener).onZenAccessPolicyChanged();
     }
 
     @Test
     public void onStop_shouldUnregisterListener() {
-        final ShadowActivityManager sam = Shadow.extract(
-                mContext.getSystemService(ActivityManager.class));
-        sam.setIsLowRamDevice(false);
-
         mLifecycle.handleLifecycleEvent(ON_START);
         mLifecycle.handleLifecycleEvent(ON_STOP);
 
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index f12e06e..c87cc25 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -17,10 +17,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -35,7 +37,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.testutils.DrawableTestHelper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -261,4 +262,19 @@
         assertThat(mPreferenceList.get(2).getCachedDevice().getAddress())
                 .isEqualTo(preference3.getCachedDevice().getAddress());
     }
+
+    @Test
+    public void onAttached_callbackNotRemoved_doNotRegisterCallback() {
+        mPreference.onAttached();
+
+        verify(mCachedBluetoothDevice, never()).unregisterCallback(any());
+    }
+
+    @Test
+    public void onAttached_callbackRemoved_registerCallback() {
+        mPreference.onPrepareForRemoval();
+        mPreference.onAttached();
+
+        verify(mCachedBluetoothDevice, times(2)).registerCallback(any());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
index da11781..20cc59d 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdaterTest.java
@@ -145,6 +145,15 @@
     }
 
     @Test
+    public void onClick_Preference_connected_setActive() {
+        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
+
+        mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
+
+        verify(mCachedBluetoothDevice).setActive();
+    }
+
+    @Test
     public void forceUpdate_findCachedBluetoothDeviceIsMatched_addPreference() {
         final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
         bluetoothDevices.add(mBluetoothDevice);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragmentTest.java
new file mode 100644
index 0000000..6261015
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDeviceDashboardFragmentTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 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.connecteddevice;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothAdapter;
+
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
+public class PreviouslyConnectedDeviceDashboardFragmentTest {
+
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+    private PreviouslyConnectedDeviceDashboardFragment mFragment;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mFragment = new PreviouslyConnectedDeviceDashboardFragment();
+        mFragment.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    @Test
+    public void onStart_bluetoothIsDisable_enableBluetooth() {
+        mShadowBluetoothAdapter.setEnabled(false);
+
+        assertThat(mFragment.mBluetoothAdapter.isEnabled()).isFalse();
+        mFragment.enableBluetoothIfNecessary();
+
+        assertThat(mFragment.mBluetoothAdapter.isEnabled()).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
index 90cbea9..d73471f 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/PreviouslyConnectedDevicePreferenceControllerTest.java
@@ -23,7 +23,10 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.pm.PackageManager;
 
@@ -32,9 +35,14 @@
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceManager;
 
+import com.android.settings.R;
+import com.android.settings.bluetooth.BluetoothDevicePreference;
 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
 import com.android.settings.connecteddevice.dock.DockUpdater;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
+import com.android.settings.widget.SingleTargetGearPreference;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,11 +51,21 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
 public class PreviouslyConnectedDevicePreferenceControllerTest {
 
-    private final String KEY = "test_key";
+    private static final String KEY = "test_key";
+    private static final String FAKE_ADDRESS_1 = "AA:AA:AA:AA:AA:01";
+    private static final String FAKE_ADDRESS_2 = "AA:AA:AA:AA:AA:02";
+    private static final String FAKE_ADDRESS_3 = "AA:AA:AA:AA:AA:03";
+    private static final String FAKE_ADDRESS_4 = "AA:AA:AA:AA:AA:04";
 
     @Mock
     private DashboardFragment mDashboardFragment;
@@ -59,10 +77,29 @@
     private PackageManager mPackageManager;
     @Mock
     private PreferenceManager mPreferenceManager;
+    @Mock
+    private Preference mSeeAllPreference;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice1;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice2;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice3;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice4;
+    @Mock
+    private BluetoothDevice mBluetoothDevice1;
+    @Mock
+    private BluetoothDevice mBluetoothDevice2;
+    @Mock
+    private BluetoothDevice mBluetoothDevice3;
+    @Mock
+    private BluetoothDevice mBluetoothDevice4;
 
     private Context mContext;
     private PreviouslyConnectedDevicePreferenceController mPreConnectedDeviceController;
     private PreferenceGroup mPreferenceGroup;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
 
     @Before
     public void setUp() {
@@ -74,11 +111,29 @@
                 new PreviouslyConnectedDevicePreferenceController(mContext, KEY);
         mPreConnectedDeviceController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater);
         mPreConnectedDeviceController.setSavedDockUpdater(mDockUpdater);
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+
+        when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1);
+        when(mCachedDevice1.getAddress()).thenReturn(FAKE_ADDRESS_1);
+        when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2);
+        when(mCachedDevice2.getAddress()).thenReturn(FAKE_ADDRESS_2);
+        when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3);
+        when(mCachedDevice3.getAddress()).thenReturn(FAKE_ADDRESS_3);
+        when(mCachedDevice4.getDevice()).thenReturn(mBluetoothDevice4);
+        when(mCachedDevice4.getAddress()).thenReturn(FAKE_ADDRESS_4);
+
+        final List<BluetoothDevice> mMostRecentlyConnectedDevices = new ArrayList<>();
+        mMostRecentlyConnectedDevices.add(mBluetoothDevice1);
+        mMostRecentlyConnectedDevices.add(mBluetoothDevice2);
+        mMostRecentlyConnectedDevices.add(mBluetoothDevice4);
+        mMostRecentlyConnectedDevices.add(mBluetoothDevice3);
+        mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(mMostRecentlyConnectedDevices);
 
         mPreferenceGroup = spy(new PreferenceCategory(mContext));
         doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager();
         mPreferenceGroup.setVisible(false);
         mPreConnectedDeviceController.setPreferenceGroup(mPreferenceGroup);
+        mPreConnectedDeviceController.mSeeAllPreference = mSeeAllPreference;
     }
 
     @Test
@@ -87,11 +142,14 @@
         mPreConnectedDeviceController.onStart();
         verify(mBluetoothDeviceUpdater).registerCallback();
         verify(mDockUpdater).registerCallback();
+        verify(mContext).registerReceiver(mPreConnectedDeviceController.mReceiver,
+                mPreConnectedDeviceController.mIntentFilter);
 
         // unregister the callback in onStop()
         mPreConnectedDeviceController.onStop();
         verify(mBluetoothDeviceUpdater).unregisterCallback();
         verify(mDockUpdater).unregisterCallback();
+        verify(mContext).unregisterReceiver(mPreConnectedDeviceController.mReceiver);
     }
 
     @Test
@@ -122,32 +180,76 @@
 
     @Test
     public void onDeviceAdded_addDevicePreference_displayIt() {
-        mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
+        final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
+                mContext, mCachedDevice1, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
 
-        assertThat(mPreferenceGroup.isVisible()).isTrue();
-        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
+        mPreConnectedDeviceController.onDeviceAdded(preference1);
+
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
+    }
+
+    @Test
+    public void onDeviceAdded_addDockDevicePreference_displayIt() {
+        final SingleTargetGearPreference dockPreference = new SingleTargetGearPreference(
+                mContext, null /* AttributeSet */);
+
+        mPreConnectedDeviceController.onDeviceAdded(dockPreference);
+
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(2);
     }
 
     @Test
     public void onDeviceAdded_addFourDevicePreference_onlyDisplayThree() {
-        mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
-        mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
-        mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
-        mPreConnectedDeviceController.onDeviceAdded(new Preference(mContext));
+        final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
+                mContext, mCachedDevice1, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+        final BluetoothDevicePreference preference2 = new BluetoothDevicePreference(
+                mContext, mCachedDevice2, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+        final BluetoothDevicePreference preference3 = new BluetoothDevicePreference(
+                mContext, mCachedDevice3, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+        final BluetoothDevicePreference preference4 = new BluetoothDevicePreference(
+                mContext, mCachedDevice4, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+        final SingleTargetGearPreference dockPreference = new SingleTargetGearPreference(
+                mContext, null /* AttributeSet */);
 
-        assertThat(mPreferenceGroup.isVisible()).isTrue();
-        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(3);
+        mPreConnectedDeviceController.onDeviceAdded(preference1);
+        mPreConnectedDeviceController.onDeviceAdded(preference2);
+        mPreConnectedDeviceController.onDeviceAdded(preference3);
+        mPreConnectedDeviceController.onDeviceAdded(preference4);
+        mPreConnectedDeviceController.onDeviceAdded(dockPreference);
+
+        // 3 BluetoothDevicePreference and 1 see all preference
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(4);
     }
 
     @Test
-    public void onDeviceRemoved_removeLastDevice_setInvisible() {
-        final Preference preference = new Preference(mContext);
-        mPreferenceGroup.addPreference(preference);
-        mPreferenceGroup.setVisible(true);
+    public void onDeviceRemoved_removeLastDevice_showSeeAllPreference() {
+        final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(
+                mContext, mCachedDevice1, true, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
+        final SingleTargetGearPreference dockPreference = new SingleTargetGearPreference(
+                mContext, null /* AttributeSet */);
+        mPreferenceGroup.addPreference(preference1);
+        mPreferenceGroup.addPreference(dockPreference);
 
-        mPreConnectedDeviceController.onDeviceRemoved(preference);
+        mPreConnectedDeviceController.onDeviceRemoved(preference1);
+        mPreConnectedDeviceController.onDeviceRemoved(dockPreference);
 
-        assertThat(mPreferenceGroup.isVisible()).isFalse();
-        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
+        assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void updatePreferenceVisibility_bluetoothIsEnable_shouldShowCorrectText() {
+        mShadowBluetoothAdapter.setEnabled(true);
+        mPreConnectedDeviceController.updatePreferenceVisibility();
+
+        verify(mSeeAllPreference).setSummary("");
+    }
+
+    @Test
+    public void updatePreferenceVisibility_bluetoothIsDisable_shouldShowCorrectText() {
+        mShadowBluetoothAdapter.setEnabled(false);
+        mPreConnectedDeviceController.updatePreferenceVisibility();
+
+        verify(mSeeAllPreference).setSummary(
+                mContext.getString(R.string.connected_device_see_all_summary));
     }
 }
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
index 4e5897d..b816a18 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/usb/UsbBackendTest.java
@@ -181,4 +181,24 @@
 
         assertThat(usbBackend.areFunctionsSupported(UsbManager.FUNCTION_MTP)).isTrue();
     }
+
+    @Test
+    public void areFunctionsDisallowedByNonAdminUser_isAdminUser_returnFalse() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+
+        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);
+
+        assertThat(usbBackend.areFunctionsDisallowedByNonAdminUser(
+                UsbManager.FUNCTION_RNDIS)).isFalse();
+    }
+
+    @Test
+    public void areFunctionsDisallowedByNonAdminUser_isNotAdminUser_returnTrue() {
+        when(mUserManager.isAdminUser()).thenReturn(false);
+
+        final UsbBackend usbBackend = new UsbBackend(mContext, mUserManager);
+
+        assertThat(usbBackend.areFunctionsDisallowedByNonAdminUser(
+                UsbManager.FUNCTION_RNDIS)).isTrue();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index 210f0fd7..4a5bc70 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -184,9 +184,10 @@
         CharSequence value = captor.getValue();
         assertThat(value.toString()).isEqualTo("512 MB data warning / 1.00 GB data limit");
 
+        // TODO (b/170330084): return intent instead of null for mSummaryPreference
         verify(mSummaryPreference).setUsageInfo((info.cycleEnd / 1000) * 1000,
                 now - UPDATE_BACKOFF_MS,
-                CARRIER_NAME, 1 /* numPlans */, intent);
+                CARRIER_NAME, 1 /* numPlans */, null /* launchIntent */);
         verify(mSummaryPreference).setChartEnabled(true);
         verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
                 false /* isSingleWifi */);
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
index 69333d7..96d0c3b 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.homepage.contextualcards;
 
+import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CONTEXTUAL_CARD_COUNT;
 import static com.android.settings.homepage.contextualcards.ContextualCardManager.KEY_CONTEXTUAL_CARDS;
 import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
 import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH;
@@ -307,7 +308,7 @@
     }
 
     @Test
-    public void onFinishCardLoading_fastLoad_shouldCallOnContextualCardUpdated() {
+    public void onFinishCardLoading_fastLoad_shouldUpdateContextualCard() {
         mManager.mStartTime = System.currentTimeMillis();
         final ContextualCardManager manager = spy(mManager);
         doNothing().when(manager).onContextualCardUpdated(anyMap());
@@ -318,7 +319,7 @@
     }
 
     @Test
-    public void onFinishCardLoading_slowLoad_shouldSkipOnContextualCardUpdated() {
+    public void onFinishCardLoading_slowLoadAndNoCard_shouldNotUpdateContextualCard() {
         mManager.mStartTime = 0;
         final ContextualCardManager manager = spy(mManager);
         doNothing().when(manager).onContextualCardUpdated(anyMap());
@@ -329,6 +330,30 @@
     }
 
     @Test
+    public void onFinishCardLoading_slowLoadAndNotPreAllocateSpace_shouldNotUpdateContextualCard() {
+        mManager.mStartTime = 0;
+        Settings.Global.putInt(mContext.getContentResolver(), CONTEXTUAL_CARD_COUNT, 3);
+        final ContextualCardManager manager = spy(mManager);
+        doNothing().when(manager).onContextualCardUpdated(anyMap());
+
+        manager.onFinishCardLoading(Arrays.asList(buildContextualCard(TEST_SLICE_URI)));
+
+        verify(manager, never()).onContextualCardUpdated(anyMap());
+    }
+
+    @Test
+    public void onFinishCardLoading_slowLoadAndPreAllocateSpace_shouldUpdateContextualCard() {
+        mManager.mStartTime = 0;
+        Settings.Global.putInt(mContext.getContentResolver(), CONTEXTUAL_CARD_COUNT, 1);
+        final ContextualCardManager manager = spy(mManager);
+        doNothing().when(manager).onContextualCardUpdated(anyMap());
+
+        manager.onFinishCardLoading(Arrays.asList(buildContextualCard(TEST_SLICE_URI)));
+
+        verify(manager).onContextualCardUpdated(anyMap());
+    }
+
+    @Test
     public void onFinishCardLoading_newLaunch_twoLoadedCards_shouldShowTwoCards() {
         mManager.mStartTime = System.currentTimeMillis();
         mManager.setListener(mListener);
diff --git a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
index 017faa5..e0e6e50 100644
--- a/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
+++ b/tests/robotests/src/com/android/settings/media/RemoteMediaSliceTest.java
@@ -32,6 +32,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 import android.net.Uri;
 
@@ -87,6 +88,7 @@
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
 
         mRemoteMediaSlice = new RemoteMediaSlice(mContext);
+        mRemoteMediaSlice.mRouterManager = mock(MediaRouter2Manager.class);
         sMediaDeviceUpdateWorker = spy(new MediaDeviceUpdateWorker(mContext,
                 REMOTE_MEDIA_SLICE_URI));
         sMediaDeviceUpdateWorker.mLocalMediaManager = mLocalMediaManager;
diff --git a/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityPreferenceControllerTest.java
new file mode 100644
index 0000000..9ba6238
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityPreferenceControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveConnectivityPreferenceControllerTest {
+
+    private static final String PREF_KEY = "adaptive_connectivity";
+
+    private Context mContext;
+    @Mock private Resources mResources;
+    private AdaptiveConnectivityPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mResources).when(mContext).getResources();
+        mController = new AdaptiveConnectivityPreferenceController(mContext, PREF_KEY);
+    }
+
+    @Test
+    public void isAvailable_supportAdaptiveConnectivity_shouldReturnTrue() {
+        when(mResources.getBoolean(R.bool.config_show_adaptive_connectivity))
+                .thenReturn(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_notSupportAdaptiveConnectivity_shouldReturnFalse() {
+        when(mResources.getBoolean(R.bool.config_show_adaptive_connectivity))
+                .thenReturn(false);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void getSummary_adaptiveConnectivityEnabled_shouldShowOn() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1);
+
+        assertThat(mController.getSummary()).isEqualTo(mContext.getString(R.string.switch_on_text));
+    }
+
+    @Test
+    public void getSummary_adaptiveConnectivityEnabled_shouldShowOff() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 0);
+
+        assertThat(mController.getSummary())
+                .isEqualTo(mContext.getString(R.string.switch_off_text));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceControllerTest.java
new file mode 100644
index 0000000..dfb9a8f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/AdaptiveConnectivityTogglePreferenceControllerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveConnectivityTogglePreferenceControllerTest {
+
+    private static final String PREF_KEY = "adaptive_connectivity";
+
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private Context mContext;
+
+    private AdaptiveConnectivityTogglePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new AdaptiveConnectivityTogglePreferenceController(mContext, PREF_KEY);
+    }
+
+    @Test
+    public void isAvailable_shouldReturnTrue() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void setChecked_withTrue_shouldUpdateSetting() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 0);
+
+        mController.setChecked(true);
+
+        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1))
+                .isEqualTo(1);
+    }
+
+    @Test
+    public void setChecked_withFalse_shouldUpdateSetting() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1);
+
+        mController.setChecked(false);
+
+        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, 1))
+                .isEqualTo(0);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
index ea957c3..7037318 100644
--- a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java
@@ -29,15 +29,12 @@
 
 import android.content.Context;
 
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceScreen;
-
 import com.android.settings.wifi.WifiConnectionPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.wifi.WifiEntryPreference;
-import com.android.wifitrackerlib.WifiEntry;
-import com.android.wifitrackerlib.WifiPickerTracker;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.AccessPointPreference;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,12 +45,19 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
 @RunWith(RobolectricTestRunner.class)
 public class WifiConnectionPreferenceControllerTest {
     private static final String KEY = "wifi_connection";
 
     @Mock
-    WifiPickerTracker mWifiPickerTracker;
+    WifiTracker mWifiTracker;
     @Mock
     PreferenceScreen mScreen;
     @Mock
@@ -70,6 +74,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+        WifiTrackerFactory.setTestingWifiTracker(mWifiTracker);
         mLifecycleOwner = () -> mLifecycle;
         mLifecycle = new Lifecycle(mLifecycleOwner);
         when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
@@ -78,51 +83,49 @@
 
         mController = new WifiConnectionPreferenceController(mContext, mLifecycle, mUpdateListener,
                 KEY, 0, 0);
-        mController.mWifiPickerTracker = mWifiPickerTracker;
     }
 
     @Test
-    public void isAvailable_noConnectedWifiEntry_availableIsFalse() {
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null);
-
+    public void isAvailable_noWiFiConnection_availableIsFalse() {
+        when(mWifiTracker.isConnected()).thenReturn(false);
         assertThat(mController.isAvailable()).isFalse();
     }
 
     @Test
-    public void displayPreference_noConnectedWifiEntry_noPreferenceAdded() {
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null);
-
+    public void displayPreference_noWiFiConnection_noPreferenceAdded() {
+        when(mWifiTracker.isConnected()).thenReturn(false);
+        when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
         mController.displayPreference(mScreen);
-
         verify(mPreferenceCategory, never()).addPreference(any());
     }
 
     @Test
-    public void displayPreference_hasConnectedWifiEntry_preferenceAdded() {
-        final WifiEntry wifiEntry = mock(WifiEntry.class);
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry);
-
+    public void displayPreference_hasWiFiConnection_preferenceAdded() {
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint = mock(AccessPoint.class);
+        when(accessPoint.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
         mController.displayPreference(mScreen);
-        verify(mPreferenceCategory).addPreference(any(WifiEntryPreference.class));
+        verify(mPreferenceCategory).addPreference(any(AccessPointPreference.class));
     }
 
     @Test
     public void onConnectedChanged_wifiBecameDisconnected_preferenceRemoved() {
-        final WifiEntry wifiEntry = mock(WifiEntry.class);
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry);
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint = mock(AccessPoint.class);
 
+        when(accessPoint.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint));
         mController.displayPreference(mScreen);
-        final ArgumentCaptor<WifiEntryPreference> captor = ArgumentCaptor.forClass(
-                WifiEntryPreference.class);
+        final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+                AccessPointPreference.class);
         verify(mPreferenceCategory).addPreference(captor.capture());
-        final WifiEntryPreference pref = captor.getValue();
+        final AccessPointPreference pref = captor.getValue();
 
-        // Become disconnected.
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null);
+        when(mWifiTracker.isConnected()).thenReturn(false);
+        when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>());
         final int onUpdatedCountBefore = mOnChildUpdatedCount;
-
-        mController.onWifiStateChanged();
-
+        mController.onConnectedChanged();
         verify(mPreferenceCategory).removePreference(pref);
         assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
     }
@@ -130,24 +133,28 @@
 
     @Test
     public void onAccessPointsChanged_wifiBecameConnectedToDifferentAP_preferenceReplaced() {
-        final WifiEntry wifiEntry1 = mock(WifiEntry.class);
-        when(wifiEntry1.getKey()).thenReturn("KEY_1");
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry1);
-        mController.displayPreference(mScreen);
-        final ArgumentCaptor<WifiEntryPreference> captor = ArgumentCaptor.forClass(
-                WifiEntryPreference.class);
+        when(mWifiTracker.isConnected()).thenReturn(true);
+        final AccessPoint accessPoint1 = mock(AccessPoint.class);
 
-        final WifiEntry wifiEntry2 = mock(WifiEntry.class);
-        when(wifiEntry1.getKey()).thenReturn("KEY_2");
-        when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry2);
+        when(accessPoint1.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1));
+        mController.displayPreference(mScreen);
+        final ArgumentCaptor<AccessPointPreference> captor = ArgumentCaptor.forClass(
+                AccessPointPreference.class);
+
+
+        final AccessPoint accessPoint2 = mock(AccessPoint.class);
+        when(accessPoint1.isActive()).thenReturn(false);
+        when(accessPoint2.isActive()).thenReturn(true);
+        when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1, accessPoint2));
         final int onUpdatedCountBefore = mOnChildUpdatedCount;
-        mController.onWifiEntriesChanged();
+        mController.onAccessPointsChanged();
 
         verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
-        final WifiEntryPreference pref1 = captor.getAllValues().get(0);
-        final WifiEntryPreference pref2 = captor.getAllValues().get(1);
-        assertThat(pref1.getWifiEntry()).isEqualTo(wifiEntry1);
-        assertThat(pref2.getWifiEntry()).isEqualTo(wifiEntry2);
+        final AccessPointPreference pref1 = captor.getAllValues().get(0);
+        final AccessPointPreference pref2 = captor.getAllValues().get(1);
+        assertThat(pref1.getAccessPoint()).isEqualTo(accessPoint1);
+        assertThat(pref2.getAccessPoint()).isEqualTo(accessPoint2);
         verify(mPreferenceCategory).removePreference(eq(pref1));
         assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1);
     }
diff --git a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java
index b2cf55b..871de0f 100644
--- a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.ON;
 
@@ -29,6 +30,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.provider.Settings;
 
@@ -44,6 +46,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
 
 @RunWith(RobolectricTestRunner.class)
 public class BubbleNotificationPreferenceControllerTest {
@@ -69,7 +73,18 @@
     }
 
     @Test
-    public void getAvilabilityStatus_returnsAvailable() {
+    public void isAvailable_lowRam_returnsUnsupported() {
+        final ShadowActivityManager activityManager =
+               Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(true);
+        assertEquals(UNSUPPORTED_ON_DEVICE, mController.getAvailabilityStatus());
+    }
+
+    @Test
+    public void isAvailable_notLowRam_returnsAvailable() {
+        final ShadowActivityManager activityManager =
+               Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(false);
         assertEquals(AVAILABLE, mController.getAvailabilityStatus());
     }
 
diff --git a/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java
index b5f505b..88de7ac 100644
--- a/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java
@@ -18,11 +18,14 @@
 
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF;
 import static com.android.settings.notification.BadgingNotificationPreferenceController.ON;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.provider.Settings;
 
@@ -35,6 +38,8 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
 
 @RunWith(RobolectricTestRunner.class)
 public class BubbleSummaryNotificationPreferenceControllerTest {
@@ -68,4 +73,20 @@
         String onString = mContext.getString(R.string.notifications_bubble_setting_on_summary);
         assertThat(mController.getSummary()).isEqualTo(onString);
     }
+
+    @Test
+    public void isAvailable_lowRam_returnsUnsupported() {
+        final ShadowActivityManager activityManager =
+                Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(true);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+    }
+
+    @Test
+    public void isAvailable_notLowRam_returnsAvailable() {
+        final ShadowActivityManager activityManager =
+                Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(false);
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
index e824505..09a40a7 100644
--- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java
@@ -30,6 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageStats;
+import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
 
 import androidx.preference.Preference;
@@ -94,6 +95,7 @@
         mContext = RuntimeEnvironment.application;
         mController = new RemoteVolumeGroupController(mContext, KEY_REMOTE_VOLUME_GROUP);
         mController.mLocalMediaManager = mLocalMediaManager;
+        mController.mRouterManager = mock(MediaRouter2Manager.class);
         mPreferenceCategory = spy(new PreferenceCategory(mContext));
         mPreferenceCategory.setKey(mController.getPreferenceKey());
 
diff --git a/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java
index d203c47..2ea240e 100644
--- a/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/BlockPreferenceControllerTest.java
@@ -368,6 +368,7 @@
     public void testOnSwitchChanged_channel_nonDefault() {
         NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+        channel.setOriginalImportance(IMPORTANCE_HIGH);
         mController.onResume(appRow, channel, null, null, null, null);
         mController.updateState(mPreference);
 
@@ -375,7 +376,7 @@
         assertEquals(IMPORTANCE_NONE, channel.getImportance());
 
         mController.onSwitchChanged(null, true);
-        assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
+        assertEquals(IMPORTANCE_HIGH, channel.getImportance());
 
         verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
     }
diff --git a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java
index 6fec525..ea963e3 100644
--- a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java
@@ -42,6 +42,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
@@ -67,7 +68,9 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowActivityManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -137,7 +140,7 @@
     }
 
     @Test
-    public void isAvailable_channel_yesIfAppOff() {
+    public void isAvailable_channel_notIfAppOff() {
         Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
         NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
         appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
@@ -145,7 +148,7 @@
         when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
         mController.onResume(appRow, channel, null, null, null, null);
 
-        assertTrue(mController.isAvailable());
+        assertFalse(mController.isAvailable());
     }
 
     @Test
@@ -159,6 +162,18 @@
     }
 
     @Test
+    public void isNotAvailable_ifLowRam() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        mController.onResume(appRow, null, null, null, null, null);
+
+        final ShadowActivityManager activityManager =
+                Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(true);
+        assertFalse(mController.isAvailable());
+    }
+
+
+    @Test
     public void isAvailable_notIfOffGlobally_channel() {
         NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
         NotificationChannel channel = mock(NotificationChannel.class);
@@ -171,6 +186,18 @@
     }
 
     @Test
+    public void isAvailable_ifNotLowRam() {
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        mController.onResume(appRow, null, null, null, null, null);
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
+
+        final ShadowActivityManager activityManager =
+                Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(false);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
     public void isAvailable_app_evenIfOffGlobally() {
         NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
         mAppPageController.onResume(appRow, null, null, null, null, null);
diff --git a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java
index af7b108..f851c96 100644
--- a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java
@@ -23,6 +23,8 @@
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
 
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
 import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_OFF;
 import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON;
 
@@ -37,6 +39,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.provider.Settings;
@@ -53,6 +56,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
 import org.robolectric.shadows.ShadowApplication;
 
 @RunWith(RobolectricTestRunner.class)
@@ -140,6 +145,28 @@
     }
 
     @Test
+    public void isAvailable_lowRam_shouldReturnFalse() {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
+        mController.onResume(mAppRow, null, null, null, null, null);
+
+        final ShadowActivityManager activityManager =
+                Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(true);
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    public void isAvailable_notLowRam_shouldReturnTrue() {
+        Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
+        mController.onResume(mAppRow, null, null, null, null, null);
+
+        final ShadowActivityManager activityManager =
+               Shadow.extract(mContext.getSystemService(ActivityManager.class));
+        activityManager.setIsLowRamDevice(false);
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
     public void updateState_setsIntent() {
         mAppRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
         mController.onResume(mAppRow, null, null, null, null, null);
diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
index 12e1f35..2d2fcc8 100644
--- a/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/ConversationPriorityPreferenceTest.java
@@ -68,10 +68,6 @@
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
                 inflater.inflate(preference.getLayoutResource(), null));
-        Drawable unselected = mock(Drawable.class);
-        Drawable selected = mock(Drawable.class);
-        preference.selectedBackground = selected;
-        preference.unselectedBackground = unselected;
 
         preference.setConfigurable(false);
         preference.setImportance(IMPORTANCE_DEFAULT);
@@ -81,35 +77,6 @@
         assertThat(holder.itemView.findViewById(R.id.silence).isEnabled()).isFalse();
         assertThat(holder.itemView.findViewById(R.id.priority_group).isEnabled()).isFalse();
         assertThat(holder.itemView.findViewById(R.id.alert).isEnabled()).isFalse();
-
-        assertThat(holder.itemView.findViewById(R.id.priority_group).getBackground())
-                .isEqualTo(selected);
-        assertThat(holder.itemView.findViewById(R.id.alert).getBackground()).isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.silence).getBackground())
-                .isEqualTo(unselected);
-
-        // other button
-        preference.setPriorityConversation(false);
-        holder = PreferenceViewHolder.createInstanceForTests(
-                inflater.inflate(preference.getLayoutResource(), null));
-        preference.onBindViewHolder(holder);
-
-        assertThat(holder.itemView.findViewById(R.id.alert).getBackground()).isEqualTo(selected);
-        assertThat(holder.itemView.findViewById(R.id.silence).getBackground())
-                .isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.priority_group).getBackground())
-                .isEqualTo(unselected);
-
-        // other other button
-        preference.setImportance(IMPORTANCE_LOW);
-        holder = PreferenceViewHolder.createInstanceForTests(
-                inflater.inflate(preference.getLayoutResource(), null));
-        preference.onBindViewHolder(holder);
-
-        assertThat(holder.itemView.findViewById(R.id.priority_group).getBackground())
-                .isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.alert).getBackground()).isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.silence).getBackground()).isEqualTo(selected);
     }
 
     @Test
@@ -119,10 +86,6 @@
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
                 inflater.inflate(preference.getLayoutResource(), null));
-        Drawable unselected = mock(Drawable.class);
-        Drawable selected = mock(Drawable.class);
-        preference.selectedBackground = selected;
-        preference.unselectedBackground = unselected;
 
         preference.setConfigurable(true);
         preference.setImportance(IMPORTANCE_LOW);
@@ -130,12 +93,8 @@
 
         preference.onBindViewHolder(holder);
 
-        assertThat(holder.itemView.findViewById(R.id.priority_group).getBackground())
-                .isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.alert).getBackground()).isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.silence).getBackground())
-                .isEqualTo(selected);
-        assertThat(holder.itemView.findViewById(R.id.silence_summary).getVisibility())
+        assertThat(holder.itemView.findViewById(R.id.silence)
+                .findViewById(R.id.summary).getVisibility())
                 .isEqualTo(View.VISIBLE);
     }
 
@@ -146,10 +105,6 @@
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
                 inflater.inflate(preference.getLayoutResource(), null));
-        Drawable unselected = mock(Drawable.class);
-        Drawable selected = mock(Drawable.class);
-        preference.selectedBackground = selected;
-        preference.unselectedBackground = unselected;
 
         preference.setConfigurable(true);
         preference.setImportance(IMPORTANCE_DEFAULT);
@@ -161,12 +116,6 @@
 
         silenceButton.callOnClick();
 
-        assertThat(holder.itemView.findViewById(R.id.alert).getBackground()).isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.priority_group).getBackground())
-                .isEqualTo(unselected);
-        assertThat(holder.itemView.findViewById(R.id.silence).getBackground())
-                .isEqualTo(selected);
-
         verify(preference, times(1)).callChangeListener(new Pair(IMPORTANCE_LOW, false));
     }
 }
diff --git a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
index 2ab428d..207a644 100644
--- a/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
+++ b/tests/robotests/src/com/android/settings/panel/MediaOutputPanelTest.java
@@ -317,4 +317,20 @@
     public void getViewType_checkType() {
         assertThat(mPanel.getViewType()).isEqualTo(PanelContent.VIEW_TYPE_SLIDER_LARGE_ICON);
     }
+
+    @Test
+    public void getIcon_mediaControllerIsNull_returnNull() {
+        mMediaControllers.clear();
+        mPanel.onStart();
+
+        assertThat(mPanel.getIcon()).isNull();
+    }
+
+    @Test
+    public void getIcon_mediaMetadataIsNull_returnNull() {
+        mPanel.onStart();
+        when(mMediaController.getMetadata()).thenReturn(null);
+
+        assertThat(mPanel.getIcon()).isNull();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
index 833d510..4a14798 100644
--- a/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
+++ b/tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +37,9 @@
 import android.view.Window;
 import android.view.WindowManager;
 
+import androidx.fragment.app.FragmentManager;
+
+import com.android.settings.R;
 import com.android.settings.core.HideNonSystemOverlayMixin;
 import com.android.settings.testutils.FakeFeatureFactory;
 
@@ -43,6 +47,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
@@ -56,6 +61,10 @@
     private FakeSettingsPanelActivity mSettingsPanelActivity;
     private PanelFeatureProvider mPanelFeatureProvider;
     private FakePanelContent mFakePanelContent;
+    @Mock
+    private PanelFragment mPanelFragment;
+    @Mock
+    private FragmentManager mFragmentManager;
 
     @Before
     public void setUp() {
@@ -67,6 +76,10 @@
         mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
         mFakePanelContent = new FakePanelContent();
         doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
+
+        mSettingsPanelActivity.mPanelFragment = mPanelFragment;
+        when(mFragmentManager.findFragmentById(R.id.main_content)).thenReturn(mPanelFragment);
+        when(mSettingsPanelActivity.getSupportFragmentManager()).thenReturn(mFragmentManager);
     }
 
     @Test
@@ -142,10 +155,61 @@
     }
 
     @Test
+    public void onStop_panelIsNotCreating_shouldForceUpdate() {
+        mSettingsPanelActivity.mForceCreation = false;
+        when(mPanelFragment.isPanelCreating()).thenReturn(false);
+        mSettingsPanelActivity.mPanelFragment = mPanelFragment;
+
+        mSettingsPanelActivity.onStop();
+
+        assertThat(mSettingsPanelActivity.mForceCreation).isTrue();
+    }
+
+    @Test
+    public void onStop_panelIsCreating_shouldNotForceUpdate() {
+        mSettingsPanelActivity.mForceCreation = false;
+        when(mPanelFragment.isPanelCreating()).thenReturn(true);
+        mSettingsPanelActivity.mPanelFragment = mPanelFragment;
+
+        mSettingsPanelActivity.onStop();
+
+        assertThat(mSettingsPanelActivity.mForceCreation).isFalse();
+    }
+
+    @Test
     public void onConfigurationChanged_shouldForceUpdate() {
         mSettingsPanelActivity.mForceCreation = false;
+
         mSettingsPanelActivity.onConfigurationChanged(new Configuration());
 
         assertThat(mSettingsPanelActivity.mForceCreation).isTrue();
     }
+
+    @Test
+    public void onNewIntent_panelIsNotCreating_shouldUpdatePanel() {
+        when(mPanelFragment.isPanelCreating()).thenReturn(false);
+
+        mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
+
+        verify(mPanelFragment).updatePanelWithAnimation();
+    }
+
+    @Test
+    public void onNewIntent_panelIsCreating_shouldNotUpdatePanel() {
+        when(mPanelFragment.isPanelCreating()).thenReturn(true);
+
+        mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
+
+        verify(mPanelFragment, never()).updatePanelWithAnimation();
+    }
+
+    @Test
+    public void onNewIntent_panelIsShowingTheSameAction_shouldNotUpdatePanel() {
+        when(mPanelFragment.isPanelCreating()).thenReturn(false);
+        when(mPanelFragment.getArguments()).thenReturn(mSettingsPanelActivity.mBundle);
+
+        mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
+
+        verify(mPanelFragment, never()).updatePanelWithAnimation();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java b/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java
new file mode 100644
index 0000000..5e22adf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/VolumeSliceHelperTest.java
@@ -0,0 +1,260 @@
+/*
+ * 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.slices;
+
+import static com.android.settings.slices.CustomSliceRegistry.VOLUME_SLICES_URI;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.net.Uri;
+
+import com.android.settings.notification.MediaVolumePreferenceController;
+import com.android.settings.notification.RingVolumePreferenceController;
+import com.android.settings.notification.VolumeSeekBarPreferenceController;
+import com.android.settingslib.SliceBroadcastRelay;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = VolumeSliceHelperTest.ShadowSliceBroadcastRelay.class)
+public class VolumeSliceHelperTest {
+
+    @Mock
+    private ContentResolver mResolver;
+
+    private Context mContext;
+    private Intent mIntent;
+    private VolumeSeekBarPreferenceController mMediaController;
+    private VolumeSeekBarPreferenceController mRingController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getContentResolver()).thenReturn(mResolver);
+
+        mMediaController = new MediaVolumePreferenceController(mContext);
+        mRingController = new RingVolumePreferenceController(mContext);
+
+        mIntent = createIntent(AudioManager.VOLUME_CHANGED_ACTION)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1)
+                .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 2)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream());
+    }
+
+    @After
+    public void cleanUp() {
+        ShadowSliceBroadcastRelay.reset();
+        VolumeSliceHelper.sRegisteredUri.clear();
+        VolumeSliceHelper.sIntentFilter = null;
+    }
+
+    @Test
+    public void registerIntentToUri_volumeController_shouldRegisterReceiver() {
+        registerIntentToUri(mMediaController);
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1);
+        assertThat(VolumeSliceHelper.sRegisteredUri)
+                .containsKey((mMediaController.getSliceUri()));
+    }
+
+    @Test
+    public void registerIntentToUri_doubleVolumeControllers_shouldRegisterReceiverOnce() {
+        registerIntentToUri(mMediaController);
+
+        registerIntentToUri(mRingController);
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1);
+        assertThat(VolumeSliceHelper.sRegisteredUri)
+                .containsKey((mRingController.getSliceUri()));
+    }
+
+    @Test
+    public void unregisterUri_notFinalUri_shouldNotUnregisterReceiver() {
+        registerIntentToUri(mMediaController);
+        registerIntentToUri(mRingController);
+
+        VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri());
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1);
+        assertThat(VolumeSliceHelper.sRegisteredUri)
+                .doesNotContainKey((mMediaController.getSliceUri()));
+    }
+
+    @Test
+    public void unregisterUri_finalUri_shouldUnregisterReceiver() {
+        registerIntentToUri(mMediaController);
+
+        VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri());
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0);
+        assertThat(VolumeSliceHelper.sRegisteredUri)
+                .doesNotContainKey((mMediaController.getSliceUri()));
+    }
+
+    @Test
+    public void unregisterUri_unregisterTwice_shouldUnregisterReceiverOnce() {
+        registerIntentToUri(mMediaController);
+
+        VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri());
+        VolumeSliceHelper.unregisterUri(mContext, mMediaController.getSliceUri());
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void unregisterUri_notRegistered_shouldNotUnregisterReceiver() {
+        registerIntentToUri(mMediaController);
+
+        VolumeSliceHelper.unregisterUri(mContext, mRingController.getSliceUri());
+
+        assertThat(ShadowSliceBroadcastRelay.getRegisteredCount()).isEqualTo(1);
+        assertThat(VolumeSliceHelper.sRegisteredUri)
+                .containsKey((mMediaController.getSliceUri()));
+    }
+
+    @Test
+    public void onReceive_audioStreamRegistered_shouldNotifyChange() {
+        registerIntentToUri(mMediaController);
+
+        VolumeSliceHelper.onReceive(mContext, mIntent);
+
+        verify(mResolver).notifyChange(mMediaController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_audioStreamNotRegistered_shouldNotNotifyChange() {
+        VolumeSliceHelper.onReceive(mContext, mIntent);
+
+        verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_audioStreamNotMatched_shouldNotNotifyChange() {
+        registerIntentToUri(mMediaController);
+        mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_DTMF);
+
+        VolumeSliceHelper.onReceive(mContext, mIntent);
+
+        verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_mediaVolumeNotChanged_shouldNotNotifyChange() {
+        mIntent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 1)
+                .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 1);
+        registerIntentToUri(mMediaController);
+
+        VolumeSliceHelper.onReceive(mContext, mIntent);
+
+        verify(mResolver, never()).notifyChange(mMediaController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_streamVolumeMuted_shouldNotifyChange() {
+        final Intent intent = createIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mMediaController.getAudioStream());
+        registerIntentToUri(mMediaController);
+        registerIntentToUri(mRingController);
+
+        VolumeSliceHelper.onReceive(mContext, intent);
+
+        verify(mResolver).notifyChange(mMediaController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_streamDevicesChanged_shouldNotifyChange() {
+        final Intent intent = createIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+                .putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mRingController.getAudioStream());
+        registerIntentToUri(mMediaController);
+        registerIntentToUri(mRingController);
+
+        VolumeSliceHelper.onReceive(mContext, intent);
+
+        verify(mResolver).notifyChange(mRingController.getSliceUri(), null);
+    }
+
+    @Test
+    public void onReceive_primaryMutedChanged_shouldNotifyChangeAll() {
+        final Intent intent = createIntent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
+        registerIntentToUri(mMediaController);
+        registerIntentToUri(mRingController);
+
+        VolumeSliceHelper.onReceive(mContext, intent);
+
+        verify(mResolver).notifyChange(mMediaController.getSliceUri(), null);
+        verify(mResolver).notifyChange(mRingController.getSliceUri(), null);
+    }
+
+    private void registerIntentToUri(VolumeSeekBarPreferenceController controller) {
+        VolumeSliceHelper.registerIntentToUri(mContext, controller.getIntentFilter(),
+                controller.getSliceUri(), controller.getAudioStream());
+    }
+
+    private Intent createIntent(String action) {
+        return new Intent(action)
+                .putExtra(SliceBroadcastRelay.EXTRA_URI, VOLUME_SLICES_URI.toString());
+    }
+
+    @Implements(SliceBroadcastRelay.class)
+    public static class ShadowSliceBroadcastRelay {
+
+        private static int sRegisteredCount;
+
+        @Implementation
+        public static void registerReceiver(Context context, Uri sliceUri,
+                Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+            sRegisteredCount++;
+        }
+
+        @Implementation
+        public static void unregisterReceivers(Context context, Uri sliceUri) {
+            sRegisteredCount--;
+        }
+
+        @Resetter
+        static void reset() {
+            sRegisteredCount = 0;
+        }
+
+        static int getRegisteredCount() {
+            return sRegisteredCount;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java
index b8cc709..f281e25 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaControlsPreferenceControllerTest.java
@@ -69,26 +69,26 @@
     }
 
     @Test
-    public void setChecked_enable_shouldTurnOff() {
+    public void setChecked_enable_shouldTurnOn() {
         Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1);
 
-        assertThat(mController.isChecked()).isFalse();
+        assertThat(mController.isChecked()).isTrue();
 
-        mController.setChecked(true);
+        mController.setChecked(false);
 
         assertThat(Settings.Secure.getInt(mContentResolver,
                 Settings.Secure.MEDIA_CONTROLS_RESUME, -1)).isEqualTo(0);
     }
 
     @Test
-    public void setChecked_disable_shouldTurnOn() {
+    public void setChecked_disable_shouldTurnOff() {
         Settings.Global.putInt(mContentResolver, Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0);
 
-        assertThat(mController.isChecked()).isTrue();
+        assertThat(mController.isChecked()).isFalse();
 
-        mController.setChecked(false);
+        mController.setChecked(true);
 
         assertThat(Settings.Secure.getInt(mContentResolver,
                 Settings.Secure.MEDIA_CONTROLS_RESUME, -1)).isEqualTo(1);
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index b6c4fb7..2c68269 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -29,6 +29,7 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
 import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
@@ -54,6 +55,7 @@
 
     public final SupportFeatureProvider supportFeatureProvider;
     public final MetricsFeatureProvider metricsFeatureProvider;
+    public final BatteryStatusFeatureProvider batteryStatusFeatureProvider;
     public final PowerUsageFeatureProvider powerUsageFeatureProvider;
     public final DashboardFeatureProvider dashboardFeatureProvider;
     public final DockUpdaterFeatureProvider dockUpdaterFeatureProvider;
@@ -98,6 +100,7 @@
     public FakeFeatureFactory() {
         supportFeatureProvider = mock(SupportFeatureProvider.class);
         metricsFeatureProvider = mock(MetricsFeatureProvider.class);
+        batteryStatusFeatureProvider = mock(BatteryStatusFeatureProvider.class);
         powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
         dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
         dockUpdaterFeatureProvider = mock(DockUpdaterFeatureProvider.class);
@@ -135,6 +138,11 @@
     }
 
     @Override
+    public BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(Context context) {
+        return batteryStatusFeatureProvider;
+    }
+
+    @Override
     public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
         return powerUsageFeatureProvider;
     }
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
index f657293..9de5c7f 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowBluetoothAdapter.java
@@ -17,6 +17,7 @@
 package com.android.settings.testutils.shadow;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
@@ -29,6 +30,7 @@
 
     private int mState;
     private List<Integer> mSupportedProfiles = new ArrayList<>();
+    private List<BluetoothDevice> mMostRecentlyConnectedDevices = new ArrayList<>();
 
     @Implementation
     protected List<Integer> getSupportedProfiles() {
@@ -56,4 +58,13 @@
     protected boolean factoryReset() {
         return true;
     }
+
+    @Implementation
+    protected List<BluetoothDevice> getMostRecentlyConnectedDevices() {
+        return mMostRecentlyConnectedDevices;
+    }
+
+    public void setMostRecentlyConnectedDevices(List<BluetoothDevice> list) {
+        mMostRecentlyConnectedDevices = list;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
index e588799..779044c 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -32,6 +33,9 @@
 import android.net.wifi.WifiManager;
 import android.os.ServiceSpecificException;
 import android.security.KeyStore;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
@@ -56,6 +60,9 @@
 import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowInputMethodManager;
+import org.robolectric.shadows.ShadowSubscriptionManager;
+
+import java.util.Arrays;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowConnectivityManager.class)
@@ -71,6 +78,7 @@
     private KeyStore mKeyStore;
     private View mView;
     private Spinner mHiddenSettingsSpinner;
+    private ShadowSubscriptionManager mShadowSubscriptionManager;
 
     public WifiConfigController2 mController;
     private static final String HEX_PSK = "01234567012345670123456701234567012345670123456701234567"
@@ -101,6 +109,7 @@
         final Spinner ipSettingsSpinner = mView.findViewById(R.id.ip_settings);
         mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
         ipSettingsSpinner.setSelection(DHCP);
+        mShadowSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class));
 
         mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
                 WifiConfigUiBase2.MODE_CONNECT);
@@ -225,6 +234,35 @@
     }
 
     @Test
+    public void isSubmittable_caCertWithoutDomain_shouldReturnFalse() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
+                WifiConfigUiBase2.MODE_CONNECT);
+        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
+        final Spinner eapCaCertSpinner = mView.findViewById(R.id.ca_cert);
+        eapCaCertSpinner.setAdapter(mController.getSpinnerAdapter(new String[]{"certificate"}));
+        eapCaCertSpinner.setSelection(0);
+        mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
+
+        assertThat(mController.isSubmittable()).isFalse();
+    }
+
+    @Test
+    public void isSubmittable_caCertWithDomain_shouldReturnTrue() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
+                WifiConfigUiBase2.MODE_CONNECT);
+        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
+        final Spinner eapCaCertSpinner = mView.findViewById(R.id.ca_cert);
+        eapCaCertSpinner.setAdapter(mController.getSpinnerAdapter(new String[]{"certificate"}));
+        eapCaCertSpinner.setSelection(0);
+        mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
+        ((TextView) mView.findViewById(R.id.domain)).setText("fakeDomain");
+
+        assertThat(mController.isSubmittable()).isTrue();
+    }
+
+    @Test
     public void getSignalString_notReachable_shouldHaveNoSignalString() {
         when(mWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE);
 
@@ -760,4 +798,41 @@
         mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
                 WifiConfigUiBase2.MODE_MODIFY);
     }
+
+    @Test
+    public void loadSims_noSim_simSpinnerDefaultNoSim() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
+                WifiConfigUiBase2.MODE_CONNECT);
+        final Spinner eapMethodSpinner = mock(Spinner.class);
+        when(eapMethodSpinner.getSelectedItemPosition()).thenReturn(
+                WifiConfigController2.WIFI_EAP_METHOD_SIM);
+        mController.mEapMethodSpinner = eapMethodSpinner;
+
+        mController.loadSims();
+
+        final WifiConfiguration wifiConfiguration = mController.getConfig();
+        assertThat(wifiConfiguration.carrierId).isEqualTo(TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    @Test
+    public void loadSims_oneSim_simSpinnerDefaultSubscription() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        final SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
+        final int carrierId = 6;
+        when(subscriptionInfo.getCarrierId()).thenReturn(carrierId);
+        when(subscriptionInfo.getCarrierName()).thenReturn("FAKE-CARRIER");
+        mShadowSubscriptionManager.setActiveSubscriptionInfoList(Arrays.asList(subscriptionInfo));
+        mController = new TestWifiConfigController2(mConfigUiBase, mView, mWifiEntry,
+                WifiConfigUiBase2.MODE_CONNECT);
+        final Spinner eapMethodSpinner = mock(Spinner.class);
+        when(eapMethodSpinner.getSelectedItemPosition()).thenReturn(
+                WifiConfigController2.WIFI_EAP_METHOD_SIM);
+        mController.mEapMethodSpinner = eapMethodSpinner;
+
+        mController.loadSims();
+
+        final WifiConfiguration wifiConfiguration = mController.getConfig();
+        assertThat(wifiConfiguration.carrierId).isEqualTo(carrierId);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
index 9146998..9e62b25 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -32,6 +33,9 @@
 import android.net.wifi.WifiManager;
 import android.os.ServiceSpecificException;
 import android.security.KeyStore;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
@@ -56,6 +60,9 @@
 import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowInputMethodManager;
+import org.robolectric.shadows.ShadowSubscriptionManager;
+
+import java.util.Arrays;
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowConnectivityManager.class)
@@ -71,6 +78,7 @@
     private KeyStore mKeyStore;
     private View mView;
     private Spinner mHiddenSettingsSpinner;
+    private ShadowSubscriptionManager mShadowSubscriptionManager;
 
     public WifiConfigController mController;
     private static final String HEX_PSK = "01234567012345670123456701234567012345670123456701234567"
@@ -95,6 +103,7 @@
         final Spinner ipSettingsSpinner = mView.findViewById(R.id.ip_settings);
         mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
         ipSettingsSpinner.setSelection(DHCP);
+        mShadowSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class));
 
         mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
                 WifiConfigUiBase.MODE_CONNECT);
@@ -219,6 +228,35 @@
     }
 
     @Test
+    public void isSubmittable_caCertWithoutDomain_shouldReturnFalse() {
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_EAP);
+        mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
+                WifiConfigUiBase.MODE_CONNECT);
+        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
+        final Spinner eapCaCertSpinner = mView.findViewById(R.id.ca_cert);
+        eapCaCertSpinner.setAdapter(mController.getSpinnerAdapter(new String[]{"certificate"}));
+        eapCaCertSpinner.setSelection(0);
+        mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
+
+        assertThat(mController.isSubmittable()).isFalse();
+    }
+
+    @Test
+    public void isSubmittable_caCertWithDomain_shouldReturnTrue() {
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_EAP);
+        mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
+                WifiConfigUiBase.MODE_CONNECT);
+        mView.findViewById(R.id.l_ca_cert).setVisibility(View.VISIBLE);
+        final Spinner eapCaCertSpinner = mView.findViewById(R.id.ca_cert);
+        eapCaCertSpinner.setAdapter(mController.getSpinnerAdapter(new String[]{"certificate"}));
+        eapCaCertSpinner.setSelection(0);
+        mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
+        ((TextView) mView.findViewById(R.id.domain)).setText("fakeDomain");
+
+        assertThat(mController.isSubmittable()).isTrue();
+    }
+
+    @Test
     public void getSignalString_notReachable_shouldHaveNoSignalString() {
         when(mAccessPoint.isReachable()).thenReturn(false);
 
@@ -575,4 +613,41 @@
 
         assertThat(advButton.getVisibility()).isEqualTo(View.GONE);
     }
+
+    @Test
+    public void loadSims_noSim_simSpinnerDefaultNoSim() {
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_EAP);
+        mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
+                WifiConfigUiBase.MODE_CONNECT);
+        final Spinner eapMethodSpinner = mock(Spinner.class);
+        when(eapMethodSpinner.getSelectedItemPosition()).thenReturn(
+                WifiConfigController2.WIFI_EAP_METHOD_SIM);
+        mController.mEapMethodSpinner = eapMethodSpinner;
+
+        mController.loadSims();
+
+        final WifiConfiguration wifiConfiguration = mController.getConfig();
+        assertThat(wifiConfiguration.carrierId).isEqualTo(TelephonyManager.UNKNOWN_CARRIER_ID);
+    }
+
+    @Test
+    public void loadSims_oneSim_simSpinnerDefaultSubscription() {
+        when(mAccessPoint.getSecurity()).thenReturn(AccessPoint.SECURITY_EAP);
+        final SubscriptionInfo subscriptionInfo = mock(SubscriptionInfo.class);
+        final int carrierId = 6;
+        when(subscriptionInfo.getCarrierId()).thenReturn(carrierId);
+        when(subscriptionInfo.getCarrierName()).thenReturn("FAKE-CARRIER");
+        mShadowSubscriptionManager.setActiveSubscriptionInfoList(Arrays.asList(subscriptionInfo));
+        mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
+                WifiConfigUiBase.MODE_CONNECT);
+        final Spinner eapMethodSpinner = mock(Spinner.class);
+        when(eapMethodSpinner.getSelectedItemPosition()).thenReturn(
+                WifiConfigController2.WIFI_EAP_METHOD_SIM);
+        mController.mEapMethodSpinner = eapMethodSpinner;
+
+        mController.loadSims();
+
+        final WifiConfiguration wifiConfiguration = mController.getConfig();
+        assertThat(wifiConfiguration.carrierId).isEqualTo(carrierId);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java
index 9de095d..dffd87d 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiUtilsTest.java
@@ -18,10 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
 import static org.mockito.Mockito.spy;
 
 import android.content.Context;
@@ -30,6 +26,9 @@
 
 import com.android.settingslib.wifi.AccessPoint;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 @RunWith(RobolectricTestRunner.class)
@@ -48,11 +47,12 @@
     public void testPassword() {
         final String longPassword = "123456789012345678901234567890"
                 + "1234567890123456789012345678901234567890";
-        assertThat(WifiUtils.isHotspotPasswordValid("123")).isFalse();
-        assertThat(WifiUtils.isHotspotPasswordValid("12345678")).isTrue();
-        assertThat(WifiUtils.isHotspotPasswordValid("1234567890")).isTrue();
-        assertThat(WifiUtils.isHotspotPasswordValid(longPassword)).isFalse();
-        assertThat(WifiUtils.isHotspotPasswordValid("")).isFalse();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid("123")).isFalse();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid("12345678")).isTrue();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid("1234567890")).isTrue();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid(longPassword)).isFalse();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid("")).isFalse();
+        assertThat(WifiUtils.isHotspotWpa2PasswordValid("€¥£")).isFalse();
     }
 
     @Test
@@ -73,4 +73,4 @@
     public void getWifiConfigWithNullInput_ThrowIllegalArgumentException() {
         WifiConfiguration config = WifiUtils.getWifiConfig(null, null, null);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
index 8d3405c..1f4254e 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -53,10 +53,12 @@
 import android.net.RouteInfo;
 import android.net.Uri;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.ImageView;
@@ -175,6 +177,8 @@
     @Mock
     private Preference mMockSsidPref;
     @Mock
+    private Preference mMockEapSimSubscriptionPref;
+    @Mock
     private Preference mMockMacAddressPref;
     @Mock
     private Preference mMockIpAddressPref;
@@ -295,7 +299,7 @@
                 .thenReturn(mMockHeaderController);
         when(mMockHeaderController.setSecondSummary(nullable(String.class)))
                 .thenReturn(mMockHeaderController);
-        when(mMockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
+        when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(new ColorDrawable());
 
         setupMockedPreferenceScreen();
     }
@@ -374,6 +378,8 @@
                 .thenReturn(mMockSecurityPref);
         when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SSID_PREF))
                 .thenReturn(mMockSsidPref);
+        when(mMockScreen.findPreference(WifiDetailPreferenceController2
+                .KEY_EAP_SIM_SUBSCRIPTION_PREF)).thenReturn(mMockEapSimSubscriptionPref);
         when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_MAC_ADDRESS_PREF))
                 .thenReturn(mMockMacAddressPref);
         when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IP_ADDRESS_PREF))
@@ -494,7 +500,7 @@
     public void entityHeader_shouldHaveIconSetForConnectedNetwork() {
         setUpForConnectedNetwork();
         setUpSpyController();
-        Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+        Drawable expectedIcon = mMockIconInjector.getIcon(false /* showX */, LEVEL);
 
         displayAndResume();
 
@@ -504,7 +510,7 @@
     @Test
     public void entityHeader_shouldHaveIconSetForDisconnectedNetwork() {
         setUpForDisconnectedNetwork();
-        Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+        Drawable expectedIcon = mMockIconInjector.getIcon(false /* showX */, LEVEL);
 
         displayAndResume();
 
@@ -609,6 +615,7 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
     }
 
@@ -618,6 +625,7 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
     }
 
@@ -627,6 +635,7 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref, never()).setIcon(any(Drawable.class));
     }
 
@@ -639,6 +648,7 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref).setSummary(expectedStrength);
     }
 
@@ -650,6 +660,7 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref).setSummary(expectedStrength);
     }
 
@@ -659,10 +670,25 @@
 
         displayAndResume();
 
+        assertThat(mController.mShowX).isFalse();
         verify(mMockSignalStrengthPref, never()).setSummary(any(String.class));
     }
 
     @Test
+    public void signalStrengthPref_shouldShowXLevelIcon_showXTrue() {
+        setUpForConnectedNetwork();
+        setUpSpyController();
+        final String expectedStrength =
+                mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+        when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
+
+        displayAndResume();
+
+        assertThat(mController.mShowX).isTrue();
+        verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+    }
+
+    @Test
     public void linkSpeedPref_shouldNotShowIfNotSet() {
         setUpForConnectedNetwork();
         setUpSpyController();
@@ -1523,7 +1549,7 @@
         ArgumentCaptor<BitmapDrawable> drawableCaptor =
                 ArgumentCaptor.forClass(BitmapDrawable.class);
         Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
-        when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+        when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(original);
 
         displayAndResume();
 
@@ -1542,7 +1568,7 @@
         ArgumentCaptor<BitmapDrawable> drawableCaptor =
                 ArgumentCaptor.forClass(BitmapDrawable.class);
         Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
-        when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+        when(mMockIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(original);
 
         displayAndResume();
 
@@ -1582,6 +1608,82 @@
         verify(mMockHeaderController).setSummary(expired);
     }
 
+    @Test
+    public void refreshEapSimSubscription_nonEapSecurity_invisibleEapSimSubscriptionPref() {
+        setUpForDisconnectedNetwork();
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref, times(1)).setVisible(false);
+
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_OWE);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref, times(2)).setVisible(false);
+
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref, times(3)).setVisible(false);
+
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_SAE);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref, times(4)).setVisible(false);
+        verify(mMockEapSimSubscriptionPref, never()).setVisible(true);
+    }
+
+    @Test
+    public void refreshEapSimSubscription_nonSimEapMethod_invisibleEapSimSubscriptionPref() {
+        setUpForDisconnectedNetwork();
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        final WifiConfiguration mockWifiConfiguration = mock(WifiConfiguration.class);
+        final WifiEnterpriseConfig mockWifiEnterpriseConfig = mock(WifiEnterpriseConfig.class);
+        when(mockWifiEnterpriseConfig.isAuthenticationSimBased()).thenReturn(false);
+        mockWifiConfiguration.enterpriseConfig = mockWifiEnterpriseConfig;
+        when(mMockWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfiguration);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref, times(1)).setVisible(false);
+    }
+
+    @Test
+    public void refreshEapSimSubscription_simEapMethod_visibleEapSimSubscriptionPref() {
+        setUpForDisconnectedNetwork();
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        final WifiConfiguration mockWifiConfiguration = mock(WifiConfiguration.class);
+        final WifiEnterpriseConfig mockWifiEnterpriseConfig = mock(WifiEnterpriseConfig.class);
+        when(mockWifiEnterpriseConfig.isAuthenticationSimBased()).thenReturn(true);
+        mockWifiConfiguration.enterpriseConfig = mockWifiEnterpriseConfig;
+        when(mMockWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfiguration);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref).setVisible(true);
+    }
+
+    @Test
+    public void refreshEapSimSubscription_unknownCarrierId_noSimEapSimSubscriptionPref() {
+        setUpForDisconnectedNetwork();
+        when(mMockWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
+        final WifiConfiguration mockWifiConfiguration = mock(WifiConfiguration.class);
+        mockWifiConfiguration.carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+        final WifiEnterpriseConfig mockWifiEnterpriseConfig = mock(WifiEnterpriseConfig.class);
+        when(mockWifiEnterpriseConfig.isAuthenticationSimBased()).thenReturn(true);
+        mockWifiConfiguration.enterpriseConfig = mockWifiEnterpriseConfig;
+        when(mMockWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfiguration);
+
+        displayAndResume();
+
+        verify(mMockEapSimSubscriptionPref).setVisible(true);
+        verify(mMockEapSimSubscriptionPref).setSummary(R.string.wifi_no_related_sim_card);
+    }
+
     private ActionButtonsPreference createMock() {
         final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
         when(pref.setButton1Text(anyInt())).thenReturn(pref);
diff --git a/tests/unit/src/com/android/settings/notification/app/ChannelListPreferenceControllerTest.java b/tests/unit/src/com/android/settings/notification/app/ChannelListPreferenceControllerTest.java
new file mode 100644
index 0000000..f9c8132
--- /dev/null
+++ b/tests/unit/src/com/android/settings/notification/app/ChannelListPreferenceControllerTest.java
@@ -0,0 +1,395 @@
+/*
+ * 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.notification.app;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import android.app.Instrumentation;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.content.Context;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.notification.NotificationBackend.NotificationsSentState;
+import com.android.settings.widget.MasterSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ChannelListPreferenceControllerTest {
+    private Context mContext;
+    private NotificationBackend mBackend;
+    private NotificationBackend.AppRow mAppRow;
+    private ChannelListPreferenceController mController;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mPreferenceScreen;
+    private PreferenceCategory mGroupList;
+
+    @Before
+    public void setUp() throws Exception {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = instrumentation.getTargetContext();
+
+        instrumentation.runOnMainSync(() -> {
+            mBackend = new NotificationBackend();
+            mAppRow = mBackend.loadAppRow(mContext,
+                    mContext.getPackageManager(), mContext.getApplicationInfo());
+            mController = new ChannelListPreferenceController(mContext, mBackend);
+            mController.onResume(mAppRow, null, null, null, null, null);
+            mPreferenceManager = new PreferenceManager(mContext);
+            mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+            mGroupList = new PreferenceCategory(mContext);
+            mPreferenceScreen.addPreference(mGroupList);
+        });
+    }
+
+    @Test
+    @UiThreadTest
+    public void testUpdateFullList_incrementalUpdates() {
+        // Start by testing the case with no groups or channels
+        List<NotificationChannelGroup> inGroups = new ArrayList<>();
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            assertEquals("zeroCategories", mGroupList.getPreference(0).getKey());
+        }
+
+        // Test that adding a group clears the zero category and adds everything
+        NotificationChannelGroup inGroup1 = new NotificationChannelGroup("group1", "Group 1");
+        inGroup1.addChannel(new NotificationChannel("ch1a", "Channel 1A", IMPORTANCE_DEFAULT));
+        inGroups.add(inGroup1);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group1", group1.getKey());
+            assertEquals(2, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+        }
+
+        // Test that adding a channel works -- no dupes or omissions
+        inGroup1.addChannel(new NotificationChannel("ch1b", "Channel 1B", IMPORTANCE_DEFAULT));
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B", group1.getPreference(2).getTitle());
+        }
+
+        // Test that renaming a channel does in fact rename the preferences
+        inGroup1.getChannels().get(1).setName("Channel 1B - Renamed");
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle());
+        }
+
+        // Test that adding a group works and results in the correct sorting.
+        NotificationChannelGroup inGroup0 = new NotificationChannelGroup("group0", "Group 0");
+        inGroup0.addChannel(new NotificationChannel("ch0b", "Channel 0B", IMPORTANCE_DEFAULT));
+        // NOTE: updateFullList takes a List which has been sorted, so we insert at 0 for this check
+        inGroups.add(0, inGroup0);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(2, mGroupList.getPreferenceCount());
+            PreferenceGroup group0 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group0", group0.getKey());
+            assertEquals(2, group0.getPreferenceCount());
+            assertNull(group0.getPreference(0).getKey());
+            assertEquals("All \"Group 0\" notifications", group0.getPreference(0).getTitle());
+            assertEquals("ch0b", group0.getPreference(1).getKey());
+            assertEquals("Channel 0B", group0.getPreference(1).getTitle());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(1);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle());
+        }
+
+        // Test that adding a channel that comes before another works and has correct ordering.
+        // NOTE: the channels within a group are sorted inside updateFullList.
+        inGroup0.addChannel(new NotificationChannel("ch0a", "Channel 0A", IMPORTANCE_DEFAULT));
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(2, mGroupList.getPreferenceCount());
+            PreferenceGroup group0 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group0", group0.getKey());
+            assertEquals(3, group0.getPreferenceCount());
+            assertNull(group0.getPreference(0).getKey());
+            assertEquals("All \"Group 0\" notifications", group0.getPreference(0).getTitle());
+            assertEquals("ch0a", group0.getPreference(1).getKey());
+            assertEquals("Channel 0A", group0.getPreference(1).getTitle());
+            assertEquals("ch0b", group0.getPreference(2).getKey());
+            assertEquals("Channel 0B", group0.getPreference(2).getTitle());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(1);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle());
+        }
+
+        // Test that the "Other" group works.
+        // Also test a simultaneous addition and deletion.
+        inGroups.remove(inGroup0);
+        NotificationChannelGroup inGroupOther = new NotificationChannelGroup(null, null);
+        inGroupOther.addChannel(new NotificationChannel("chXa", "Other A", IMPORTANCE_DEFAULT));
+        inGroupOther.addChannel(new NotificationChannel("chXb", "Other B", IMPORTANCE_DEFAULT));
+        inGroups.add(inGroupOther);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(2, mGroupList.getPreferenceCount());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle());
+            PreferenceGroup groupOther = (PreferenceGroup) mGroupList.getPreference(1);
+            assertEquals("categories", groupOther.getKey());
+            assertEquals(2, groupOther.getPreferenceCount());
+            assertEquals("chXa", groupOther.getPreference(0).getKey());
+            assertEquals("Other A", groupOther.getPreference(0).getTitle());
+            assertEquals("chXb", groupOther.getPreference(1).getKey());
+            assertEquals("Other B", groupOther.getPreference(1).getTitle());
+        }
+
+        // Test that the removal of a channel works.
+        inGroupOther.getChannels().remove(0);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(2, mGroupList.getPreferenceCount());
+            PreferenceGroup group1 = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group1", group1.getKey());
+            assertEquals(3, group1.getPreferenceCount());
+            assertNull(group1.getPreference(0).getKey());
+            assertEquals("All \"Group 1\" notifications", group1.getPreference(0).getTitle());
+            assertEquals("ch1a", group1.getPreference(1).getKey());
+            assertEquals("Channel 1A", group1.getPreference(1).getTitle());
+            assertEquals("ch1b", group1.getPreference(2).getKey());
+            assertEquals("Channel 1B - Renamed", group1.getPreference(2).getTitle());
+            PreferenceGroup groupOther = (PreferenceGroup) mGroupList.getPreference(1);
+            assertEquals("categories", groupOther.getKey());
+            assertEquals(1, groupOther.getPreferenceCount());
+            assertEquals("chXb", groupOther.getPreference(0).getKey());
+            assertEquals("Other B", groupOther.getPreference(0).getTitle());
+        }
+
+        // Test that we go back to the empty state when clearing all groups and channels.
+        inGroups.clear();
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            assertEquals("zeroCategories", mGroupList.getPreference(0).getKey());
+        }
+    }
+
+
+    @Test
+    @UiThreadTest
+    public void testUpdateFullList_groupBlockedChange() {
+        List<NotificationChannelGroup> inGroups = new ArrayList<>();
+        NotificationChannelGroup inGroup = new NotificationChannelGroup("group", "My Group");
+        inGroup.addChannel(new NotificationChannel("channelA", "Channel A", IMPORTANCE_DEFAULT));
+        inGroup.addChannel(new NotificationChannel("channelB", "Channel B", IMPORTANCE_NONE));
+        inGroups.add(inGroup);
+
+        // Test that the group is initially showing all preferences
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group", group.getKey());
+            assertEquals(3, group.getPreferenceCount());
+            SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
+            assertNull(groupBlockPref.getKey());
+            assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle());
+            assertTrue(groupBlockPref.isChecked());
+            MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1);
+            assertEquals("channelA", channelAPref.getKey());
+            assertEquals("Channel A", channelAPref.getTitle());
+            assertEquals(Boolean.TRUE, channelAPref.getCheckedState());
+            MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2);
+            assertEquals("channelB", channelBPref.getKey());
+            assertEquals("Channel B", channelBPref.getTitle());
+            assertEquals(Boolean.FALSE, channelBPref.getCheckedState());
+        }
+
+        // Test that when a group is blocked, the list removes its individual channel preferences
+        inGroup.setBlocked(true);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group", group.getKey());
+            assertEquals(1, group.getPreferenceCount());
+            SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
+            assertNull(groupBlockPref.getKey());
+            assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle());
+            assertFalse(groupBlockPref.isChecked());
+        }
+
+        // Test that when a group is unblocked, the list adds its individual channel preferences
+        inGroup.setBlocked(false);
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group", group.getKey());
+            assertEquals(3, group.getPreferenceCount());
+            SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
+            assertNull(groupBlockPref.getKey());
+            assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle());
+            assertTrue(groupBlockPref.isChecked());
+            MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1);
+            assertEquals("channelA", channelAPref.getKey());
+            assertEquals("Channel A", channelAPref.getTitle());
+            assertEquals(Boolean.TRUE, channelAPref.getCheckedState());
+            MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2);
+            assertEquals("channelB", channelBPref.getKey());
+            assertEquals("Channel B", channelBPref.getTitle());
+            assertEquals(Boolean.FALSE, channelBPref.getCheckedState());
+        }
+    }
+
+    @Test
+    @UiThreadTest
+    public void testUpdateFullList_channelUpdates() {
+        List<NotificationChannelGroup> inGroups = new ArrayList<>();
+        NotificationChannelGroup inGroup = new NotificationChannelGroup("group", "Group");
+        NotificationChannel channelA =
+                new NotificationChannel("channelA", "Channel A", IMPORTANCE_HIGH);
+        NotificationChannel channelB =
+                new NotificationChannel("channelB", "Channel B", IMPORTANCE_NONE);
+        inGroup.addChannel(channelA);
+        inGroup.addChannel(channelB);
+        inGroups.add(inGroup);
+
+        NotificationsSentState sentA = new NotificationsSentState();
+        sentA.avgSentDaily = 2;
+        sentA.avgSentWeekly = 10;
+        NotificationsSentState sentB = new NotificationsSentState();
+        sentB.avgSentDaily = 0;
+        sentB.avgSentWeekly = 2;
+        mAppRow.sentByChannel.put("channelA", sentA);
+
+        // Test that the channels' properties are reflected in the preference
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group", group.getKey());
+            assertEquals(3, group.getPreferenceCount());
+            assertNull(group.getPreference(0).getKey());
+            assertEquals("All \"Group\" notifications", group.getPreference(0).getTitle());
+            MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1);
+            assertEquals("channelA", channelAPref.getKey());
+            assertEquals("Channel A", channelAPref.getTitle());
+            assertEquals(Boolean.TRUE, channelAPref.getCheckedState());
+            assertEquals("~2 notifications per day", channelAPref.getSummary());
+            assertNotNull(channelAPref.getIcon());
+            MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2);
+            assertEquals("channelB", channelBPref.getKey());
+            assertEquals("Channel B", channelBPref.getTitle());
+            assertEquals(Boolean.FALSE, channelBPref.getCheckedState());
+            assertNull(channelBPref.getSummary());
+            assertNull(channelBPref.getIcon());
+        }
+
+        channelA.setImportance(IMPORTANCE_NONE);
+        channelB.setImportance(IMPORTANCE_DEFAULT);
+
+        mAppRow.sentByChannel.remove("channelA");
+        mAppRow.sentByChannel.put("channelB", sentB);
+
+        // Test that changing the channels' properties correctly updates the preference
+        mController.updateFullList(mGroupList, inGroups);
+        {
+            assertEquals(1, mGroupList.getPreferenceCount());
+            PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
+            assertEquals("group", group.getKey());
+            assertEquals(3, group.getPreferenceCount());
+            assertNull(group.getPreference(0).getKey());
+            assertEquals("All \"Group\" notifications", group.getPreference(0).getTitle());
+            MasterSwitchPreference channelAPref = (MasterSwitchPreference) group.getPreference(1);
+            assertEquals("channelA", channelAPref.getKey());
+            assertEquals("Channel A", channelAPref.getTitle());
+            assertEquals(Boolean.FALSE, channelAPref.getCheckedState());
+            assertNull(channelAPref.getSummary());
+            assertNull(channelAPref.getIcon());
+            MasterSwitchPreference channelBPref = (MasterSwitchPreference) group.getPreference(2);
+            assertEquals("channelB", channelBPref.getKey());
+            assertEquals("Channel B", channelBPref.getTitle());
+            assertEquals(Boolean.TRUE, channelBPref.getCheckedState());
+            assertEquals("~2 notifications per week", channelBPref.getSummary());
+            assertNotNull(channelBPref.getIcon());
+        }
+    }
+
+}