Merge "Show/hide 5G preferred network type also consider allowed_network_type"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9bb810d..1f9c650 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1193,6 +1193,15 @@
                 android:value="com.android.settings.notification.history.NotificationStation" />
         </activity>
 
+        <activity
+            android:name=".notification.history.NotificationHistoryActivity"
+            android:label="@string/notification_history_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".notification.zen.ZenModeVoiceActivity"
                 android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"
                 android:label="@string/zen_mode_settings_title">
@@ -2858,8 +2867,7 @@
 
         <activity
             android:name="Settings$AppManageExternalStorageActivity"
-            android:label="@string/manage_external_storage_title"
-            android:permission="android.permission.MANAGE_EXTERNAL_STORAGE">
+            android:label="@string/manage_external_storage_title">
             <intent-filter android:priority="1">
                 <action android:name="android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png b/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png
new file mode 100644
index 0000000..a8d86aa
--- /dev/null
+++ b/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png
Binary files differ
diff --git a/res/drawable/ic_clear.xml b/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..224425f
--- /dev/null
+++ b/res/drawable/ic_clear.xml
@@ -0,0 +1,26 @@
+<?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.
+    -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:width="24dp"
+    android:height="24dp">
+    <path
+        android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"
+        android:fillColor="#FFFFFF" />
+</vector>
+
diff --git a/res/drawable/ic_snooze.xml b/res/drawable/ic_snooze.xml
new file mode 100644
index 0000000..d7bbbaf
--- /dev/null
+++ b/res/drawable/ic_snooze.xml
@@ -0,0 +1,24 @@
+<!--
+  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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M9,11h3.63L9,15.2L9,17h6v-2h-3.63L15,10.8L15,9L9,9v2zM16.056,3.346l1.282,-1.535 4.607,3.85 -1.28,1.54zM3.336,7.19l-1.28,-1.536L6.662,1.81l1.28,1.536zM12,6c3.86,0 7,3.14 7,7s-3.14,7 -7,7 -7,-3.14 -7,-7 3.14,-7 7,-7m0,-2c-4.97,0 -9,4.03 -9,9s4.03,9 9,9 9,-4.03 9,-9 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/res/drawable/ic_today.xml b/res/drawable/ic_today.xml
new file mode 100644
index 0000000..1d39610
--- /dev/null
+++ b/res/drawable/ic_today.xml
@@ -0,0 +1,24 @@
+<!--
+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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11zM9,9.5c-1.38,0 -2.5,1.12 -2.5,2.5s1.12,2.5 2.5,2.5 2.5,-1.12 2.5,-2.5S10.38,9.5 9,9.5z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/rounded_bg.xml b/res/drawable/rounded_bg.xml
new file mode 100644
index 0000000..fcdd761
--- /dev/null
+++ b/res/drawable/rounded_bg.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners
+        android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:bottomRightRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
+    />
+</shape>
\ No newline at end of file
diff --git a/res/layout/accessibility_edit_shortcut_component.xml b/res/layout/accessibility_edit_shortcut_component.xml
index 52b8935..dd92a1d 100644
--- a/res/layout/accessibility_edit_shortcut_component.xml
+++ b/res/layout/accessibility_edit_shortcut_component.xml
@@ -26,20 +26,25 @@
         android:id="@+id/checkbox"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:saveEnabled="false"/>
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:saveEnabled="false"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:textColor="?android:attr/textColorAlertDialogListItem" />
 
     <TextView
         android:id="@+id/summary"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textColor="?android:attr/textColorSecondary"
-        android:paddingStart="32dp"/>
+        android:paddingBottom="8dp"
+        android:paddingStart="32dp"
+        android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+        android:textColor="?android:attr/textColorSecondary" />
 
     <ImageView
         android:id="@+id/image"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:paddingStart="32dp"
-        android:scaleType="fitCenter"/>
+        android:scaleType="fitCenter" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/layout/accessibility_edit_shortcut_magnification.xml b/res/layout/accessibility_edit_shortcut_magnification.xml
index 76a1fad..8a724cc 100644
--- a/res/layout/accessibility_edit_shortcut_magnification.xml
+++ b/res/layout/accessibility_edit_shortcut_magnification.xml
@@ -52,8 +52,10 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:gravity="center_vertical"
-                android:textColor="?android:attr/colorAccent"
-                android:text="@string/accessibility_shortcut_edit_dialog_title_advance"/>
+                android:minHeight="?android:attr/listPreferredItemHeightSmall"
+                android:text="@string/accessibility_shortcut_edit_dialog_title_advance"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:textColor="?android:attr/colorAccent" />
 
         </LinearLayout>>
 
diff --git a/res/layout/contextual_slice_deferred_setup.xml b/res/layout/contextual_slice_deferred_setup.xml
deleted file mode 100644
index 7d5b688..0000000
--- a/res/layout/contextual_slice_deferred_setup.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2019 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<com.google.android.material.card.MaterialCardView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/ContextualCardStyle"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <LinearLayout
-        android:id="@+id/content"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="start"
-        android:orientation="vertical"
-        android:paddingEnd="@dimen/contextual_card_padding_end"
-        android:paddingTop="@dimen/contextual_deferred_setup_card_padding_top"
-        android:paddingBottom="@dimen/contextual_deferred_setup_card_padding_bottom">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="@dimen/contextual_card_icon_size"
-            android:layout_height="@dimen/contextual_card_icon_size"
-            android:layout_marginStart="@dimen/contextual_card_icon_padding_start"/>
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/contextual_card_text_padding_start"
-            android:layout_marginTop="@dimen/contextual_deferred_setup_card_title_margin_top"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:minLines="1"
-            android:textAppearance="@style/TextAppearance.DeferredSetupCardTitle"/>
-
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/contextual_card_text_padding_start"
-            android:layout_marginTop="@dimen/contextual_deferred_setup_card_summary_margin_top"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:minLines="1"
-            android:textAppearance="@style/TextAppearance.DeferredSetupCardSummary"/>
-
-        <Button
-            android:id="@+id/finish_setup"
-            style="@style/DeferredSetupCardButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/contextual_card_text_padding_start"
-            android:layout_marginTop="@dimen/contextual_deferred_setup_card_button_margin_top"
-            android:text="@string/suggestion_button_text"/>
-
-    </LinearLayout>
-</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
diff --git a/res/layout/contextual_slice_sticky_tile.xml b/res/layout/contextual_slice_sticky_tile.xml
new file mode 100644
index 0000000..8e82f53
--- /dev/null
+++ b/res/layout/contextual_slice_sticky_tile.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<com.google.android.material.card.MaterialCardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="@style/ContextualCardStyle">
+
+    <androidx.slice.widget.SliceView
+        android:id="@+id/slice_view"
+        style="@style/ContextualCardSliceViewStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:importantForAccessibility="no"/>
+
+</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
diff --git a/res/layout/notification_history.xml b/res/layout/notification_history.xml
new file mode 100644
index 0000000..91a59a8
--- /dev/null
+++ b/res/layout/notification_history.xml
@@ -0,0 +1,161 @@
+<?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.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/scroll"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:background="@*android:color/material_grey_50">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- TODO: header switch -->
+        <LinearLayout
+            android:id="@+id/snoozed_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="10dp">
+            <LinearLayout
+                android:id="@+id/snooze_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_snooze"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_snooze"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <FrameLayout
+                android:id="@+id/list_container"
+                android:layout_width="wrap_content"
+                android:layout_height="300dp"
+                android:clipChildren="true"
+                android:elevation="3dp"
+                android:background="@drawable/rounded_bg">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/notification_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:clipChildren="true"
+                    settings:fastScrollEnabled="true"
+                    settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+                    settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+            </FrameLayout>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/recently_dismissed_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_marginBottom="10dp">
+            <LinearLayout
+                android:id="@+id/dismissed_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_clear"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_dismiss"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <FrameLayout
+                android:id="@+id/list_container"
+                android:layout_width="wrap_content"
+                android:layout_height="300dp"
+                android:elevation="3dp"
+                android:clipChildren="true"
+                android:background="@drawable/rounded_bg">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/notification_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:clipChildren="true"
+                    settings:fastScrollEnabled="true"
+                    settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+                    settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+                    settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+            </FrameLayout>
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/today_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <LinearLayout
+                android:id="@+id/app_header"
+                android:layout_height="48dp"
+                android:layout_width="match_parent"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+                <ImageView
+                    android:src="@drawable/ic_today"
+                    android:tint="?android:attr/textColorPrimary"
+                    android:padding="6dp"
+                    android:layout_height="36dp"
+                    android:layout_width="36dp" />
+                <TextView
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:text="@string/notification_history_today"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:paddingStart="@dimen/notification_history_header_drawable_start" />
+            </LinearLayout>
+            <LinearLayout
+                android:id="@+id/apps"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:elevation="3dp"
+                android:orientation="vertical"
+                android:background="@drawable/rounded_bg">
+                <!-- app based recycler views added here -->
+            </LinearLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout/notification_history_app_layout.xml b/res/layout/notification_history_app_layout.xml
new file mode 100644
index 0000000..6f26789
--- /dev/null
+++ b/res/layout/notification_history_app_layout.xml
@@ -0,0 +1,77 @@
+<?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.
+    -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <RelativeLayout
+        android:id="@+id/app_header"
+        android:layout_height="48dp"
+        android:layout_width="match_parent">
+        <ImageView
+            android:id="@+id/icon"
+            android:padding="6dp"
+            android:layout_centerVertical="true"
+            android:layout_height="36dp"
+            android:layout_width="36dp" />
+        <TextView
+            android:id="@+id/label"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_centerVertical="true"
+
+            android:textColor="@color/material_grey_900"
+            android:layout_toEndOf="@id/icon"
+            android:paddingStart="@dimen/notification_history_header_drawable_start" />
+        <ImageButton
+            android:id="@+id/expand"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:layout_height="48dp"
+            android:layout_width="48dp"
+            android:src="@*android:drawable/ic_expand_more"/>
+    </RelativeLayout>
+
+    <FrameLayout
+        android:id="@+id/list_container"
+        android:layout_width="match_parent"
+        android:clipChildren="true"
+        android:layout_height="300dp">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/notification_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipChildren="true"
+            settings:fastScrollEnabled="true"
+            settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+            settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+            settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+            settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+    </FrameLayout>
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:background="@color/material_grey_300" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/notification_history_log_row.xml b/res/layout/notification_history_log_row.xml
new file mode 100644
index 0000000..4c38167
--- /dev/null
+++ b/res/layout/notification_history_log_row.xml
@@ -0,0 +1,93 @@
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginBottom="6dp">
+        <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:gravity="center_vertical">
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="@color/material_grey_900"
+                android:textAlignment="viewStart"/>
+
+            <ImageView
+                android:id="@+id/alerted_icon"
+                android:layout_width="@*android:dimen/status_bar_icon_size"
+                android:layout_height="@*android:dimen/status_bar_icon_size"
+                android:layout_centerVertical="true"
+                android:layout_marginStart="6dp"
+                android:paddingTop="1dp"
+                android:scaleType="fitCenter"
+                android:visibility="gone"
+                android:layout_toEndOf="@id/title"
+                android:tint="?android:attr/textColorSecondary"
+                android:src="@drawable/ic_notifications_alert"/>
+
+            <DateTimeView
+                android:id="@+id/timestamp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_centerVertical="true"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textAlignment="viewEnd"
+                />
+        </RelativeLayout>
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewStart" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="0.5dp"
+            android:layout_marginTop="17dp"
+            android:background="@color/material_grey_300" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/notification_sbn_log_row.xml b/res/layout/notification_sbn_log_row.xml
new file mode 100644
index 0000000..3c51abd
--- /dev/null
+++ b/res/layout/notification_sbn_log_row.xml
@@ -0,0 +1,142 @@
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="6dp"
+    android:paddingTop="6dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        >
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@+id/pkgicon"
+            android:layout_marginStart="0dp"
+            android:layout_marginEnd="8dp"
+            android:contentDescription="@null"
+            android:adjustViewBounds="true"
+            android:tint="?android:attr/textColorPrimary"
+            android:maxHeight="@*android:dimen/status_bar_icon_size"
+            android:maxWidth="@*android:dimen/status_bar_icon_size"
+            android:scaleType="fitCenter" />
+
+        <TextView
+            android:id="@+id/pkgname"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/icon"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textStyle="bold"
+            android:textAlignment="viewStart" />
+
+        <ImageView
+            android:id="@+id/alerted_icon"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="6dp"
+            android:paddingTop="1dp"
+            android:scaleType="fitCenter"
+            android:visibility="gone"
+            android:layout_toEndOf="@id/pkgname"
+            android:tint="?android:attr/textColorSecondary"
+            android:src="@drawable/ic_notifications_alert"
+        />
+
+        <ImageView
+            android:id="@+id/profile_badge"
+            android:layout_width="@*android:dimen/status_bar_icon_size"
+            android:layout_height="@*android:dimen/status_bar_icon_size"
+            android:layout_centerVertical="true"
+            android:layout_marginEnd="6dp"
+            android:paddingTop="1dp"
+            android:scaleType="fitCenter"
+            android:visibility="gone"
+            android:layout_toStartOf="@id/timestamp"
+        />
+
+        <DateTimeView
+            android:id="@+id/timestamp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="13dp"
+            android:paddingBottom="13dp"
+            android:layout_alignBottom="@android:id/widget_frame"
+            android:layout_alignParentEnd="true"
+            android:layout_alignTop="@android:id/widget_frame"
+            android:layout_centerVertical="true"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewEnd"
+            />
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_marginStart="30dp"
+        android:layout_marginBottom="6dp"
+        >
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAlignment="viewStart"
+        />
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="left|center_vertical"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAlignment="viewStart"
+        />
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:layout_marginTop="17dp"
+        android:background="@color/material_grey_300" />
+</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 4440358..396a485 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -106,6 +106,8 @@
         <item>@string/dark_ui_auto_mode_never</item>
         <!-- 1: Auto -->
         <item>@string/dark_ui_auto_mode_auto</item>
+        <!-- 2: Custom -->
+        <item>@string/dark_ui_auto_mode_custom</item>
     </string-array>
 
     <!-- Security settings.  The delay after screen is turned off until device locks.
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b8afd60..f79874c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,6 +81,8 @@
     <dimen name="notification_importance_button_text">16sp</dimen>
     <dimen name="notification_importance_button_padding">14dp</dimen>
 
+    <dimen name="notification_history_header_drawable_start">26dp</dimen>
+
     <dimen name="zen_mode_button_padding_vertical">16dp</dimen>
     <dimen name="zen_schedule_rule_checkbox_padding">7dp</dimen>
     <dimen name="zen_schedule_day_margin">17dp</dimen>
@@ -342,15 +344,6 @@
     <dimen name="contextual_half_card_padding_top">12dp</dimen>
     <dimen name="contextual_half_card_padding_bottom">16dp</dimen>
     <dimen name="contextual_half_card_title_margin_top">12dp</dimen>
-    <dimen name="contextual_deferred_setup_card_padding_top">16dp</dimen>
-    <dimen name="contextual_deferred_setup_card_padding_bottom">12dp</dimen>
-    <dimen name="contextual_deferred_setup_card_title_margin_top">12dp</dimen>
-    <dimen name="contextual_deferred_setup_card_summary_margin_top">2dp</dimen>
-    <dimen name="contextual_deferred_setup_card_button_margin_top">8dp</dimen>
-    <dimen name="contextual_deferred_setup_card_button_padding_top">8dp</dimen>
-    <dimen name="contextual_deferred_setup_card_button_padding_bottom">8dp</dimen>
-    <dimen name="contextual_deferred_setup_card_button_padding_start">24dp</dimen>
-    <dimen name="contextual_deferred_setup_card_button_padding_end">24dp</dimen>
 
     <!-- Homepage dismissal cards size and padding -->
     <dimen name="contextual_card_dismissal_margin_top">12dp</dimen>
@@ -400,6 +393,8 @@
     <!-- Maximum height for SliceView, override on slices/view/src/main/res/values/dimens.xml -->
     <!-- A single Row Slice height is 60dp -->
     <dimen name="abc_slice_large_height">1200dp</dimen>
+    <!-- Min height of slice row view, override on slices/view/src/main/res/values/dimens.xml -->
+    <dimen name="abc_slice_row_min_height">@dimen/abc_slice_row_max_height</dimen>
 
     <!-- System navigation settings illustration height -->
     <dimen name="system_navigation_illustration_height">320dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 330ada4..eede918 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -144,8 +144,6 @@
     <string name="bluetooth_lock_voice_dialing_summary">
       Prevent use of the bluetooth dialer when the screen is locked
     </string>
-    <!-- Bluetooth settings screen, heading above the list of nearby bluetooth devices -->
-    <string name="bluetooth_devices">Bluetooth devices</string>
     <!-- Bluetooth settings screen, title for the current bluetooth name setting -->
     <string name="bluetooth_device_name">Device name</string>
     <!-- Bluetooth settings screen, image description for device details button. This opens the screen to rename, unpair, etc. a single device. -->
@@ -2764,7 +2762,7 @@
     <string name="night_display_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
     <!-- Display settings screen, summary of night display when off and will *never* turn on automatically. [CHAR LIMIT=NONE] -->
     <string name="night_display_summary_off_auto_mode_never">Will never turn on automatically</string>
-    <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
+         <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
     <string name="night_display_summary_off_auto_mode_custom">Will turn on automatically at <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
     <!-- Display settings screen, summary of night display when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
     <string name="night_display_summary_off_auto_mode_twilight">Will turn on automatically at sunset</string>
@@ -2806,9 +2804,11 @@
     <string name="dark_ui_auto_mode_title">Schedule</string>
     <!-- Dark UI screen, setting option value for Dark UI to *never* turn on/off automatically. [CHAR LIMIT=30] -->
     <string name="dark_ui_auto_mode_never">None</string>
-    <!-- Dark UIscreen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
+    <!-- Dark UI screen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
     <string name="dark_ui_auto_mode_auto">Turns on from sunset to sunrise</string>
-    <!-- Dark UIscreen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
+    <!-- Dark UI screen, setting option value for Dark theme to turn on/off automatically according to a user defined schedule. [CHAR LIMIT=32] -->
+    <string name="dark_ui_auto_mode_custom">Turns on at custom time</string>
+    <!-- Dark UI screen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
     <string name="dark_ui_status_title">Status</string>
     <!-- Display settings screen, summary format of Dark UI when off. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
@@ -2816,12 +2816,20 @@
     <string name="dark_ui_summary_off_auto_mode_never">Will never turn on automatically</string>
     <!-- Display settings screen, summary of Dark UI when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_summary_off_auto_mode_auto">Will turn on automatically at sunset</string>
+        <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
+    <string name="dark_ui_summary_off_auto_mode_custom">Will turn on automatically at <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
     <!-- Display settings screen, summary format of Dark UI when on. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_summary_on">On / <xliff:g name="auto_mode_summary" example="Never turn off automatically">%1$s</xliff:g></string>
     <!-- Display settings screen, summary of Dark UI when on and will *never* turn off automatically. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_summary_on_auto_mode_never">Will never turn off automatically</string>
     <!-- Display settings screen, summary of Dark UI when on and will turn off automatically at sunrise. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_summary_on_auto_mode_auto">Will turn off automatically at sunrise</string>
+    <!-- Display settings screen, summary format of night display when on and will turn off automatically at a user defined time. [CHAR LIMIT=NONE] -->
+    <string name="dark_ui_summary_on_auto_mode_custom">Will turn off automatically at <xliff:g name="time" example="10 PM">%1$s</xliff:g></string>
+    <!-- Display settings screen, activation button action for custom schedule [CHAR LIMIT=40] -->
+    <string name="dark_ui_activation_on_custom">Turn on until <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
+    <!-- Display settings screen, deactivation button action for custom schedule [CHAR LIMIT=40] -->
+    <string name="dark_ui_activation_off_custom">Turn off until <xliff:g name="time" example="10 PM">%1$s</xliff:g></string>
     <!-- Dark theme screen, description of Dark theme feature. [CHAR LIMIT=NONE] -->
     <string name="dark_ui_text">Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off.</string>
 
@@ -4795,8 +4803,8 @@
     <string name="accessibility_screen_magnification_navbar_title">Magnify with shortcut</string>
     <!-- Summary for the accessibility magnification setting indicating both "Magnify with button" and "Magnify with triple-tap" are enabled [CHAR LIMIT=50] -->
     <string name="accessibility_screen_magnification_state_navbar_gesture">Magnify with shortcut &amp; triple-tap</string>
-    <!-- Title for the footer text to explain what Magnify does. [CHAR LIMIT=35] -->
-    <string name="accessibility_screen_magnification_about">About Magnify</string>
+    <!-- Title for the footer text to explain what accessibility service does. [CHAR LIMIT=35] -->
+    <string name="accessibility_footer_title">About <xliff:g id="service" example="Select to Speak">%1$s</xliff:g></string>
     <!-- Title for the footer text to explain what option accessibility service does. [CHAR LIMIT=35] -->
     <string name="accessibility_screen_option">Options</string>
     <!-- Summary for the accessibility preference to enable screen magnification. [CHAR LIMIT=25] -->
@@ -4818,13 +4826,13 @@
     <!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using the 3-button nav bar. [CHAR LIMIT=NONE] -->
     <string name="accessibility_tutorial_dialog_message_button">To turn this service on or off, tap the accessibility button <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> on the bottom of your screen.\n\nTo switch between services, touch &amp; hold the accessibility button.</string>
     <!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is not enabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_tutorial_dialog_message_gesture_without_talkback">To turn this service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
+    <string name="accessibility_tutorial_dialog_message_gesture">To turn this service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
     <!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is enabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_tutorial_dialog_message_gesture_with_talkback">To turn this service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
+    <string name="accessibility_tutorial_dialog_message_gesture_talkback">To turn this service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
     <!-- Message for the Accessibility tutorial dialog when user chooses gesture navigation in navigation settings, an accessibility service is using the accessibility button, and touch exploration is disabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_tutorial_dialog_message_gesture_settings_without_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
+    <string name="accessibility_tutorial_dialog_message_gesture_settings">To turn an accessibility service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
     <!-- Message for the Accessibility tutorial dialog when user chooses gesture navigation in navigation settings, an accessibility service is using the accessibility button, and touch exploration is enabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_tutorial_dialog_message_gesture_settings_with_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
+    <string name="accessibility_tutorial_dialog_message_gesture_settings_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
     <!-- Button for the Accessibility tutorial dialog to dismiss the dialog when user clicks it. [CHAR LIMIT=10] -->
     <string name="accessibility_tutorial_dialog_button">Got it</string>
     <!-- Title for accessibility shortcut preference for accessibility apps. [CHAR LIMIT=40] -->
@@ -4839,12 +4847,16 @@
     <string name="accessibility_shortcut_edit_dialog_title_daltonizer">Shortcut to open Color correction</string>
     <!-- Title for software shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_title_software">Accessibility Button</string>
-    <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+    <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation is enabled. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_title_software_gesture">2-finger swipe up from bottom</string>
+    <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_edit_dialog_title_software_gesture_talkback">3-finger swipe up from bottom</string>
     <!-- Summary for software shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_summary_software">Tap the <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> button at the bottom of your screen</string>
-    <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+    <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation is enabled. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_summary_software_gesture">Swipe up with 2 fingers from edge of screen</string>
+    <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_edit_dialog_summary_software_gesture_talkback">Swipe up with 3 fingers from edge of screen</string>
     <!-- Title for hardware shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_title_hardware">Hold volume keys</string>
     <!-- Summary for hardware shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
@@ -4948,7 +4960,7 @@
     <!-- Used in the accessibility service settings to control turning on/off the service entirely -->
     <string name="accessibility_service_master_switch_title">Use <xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g></string>
     <!-- Used in the Color correction settings screen to control turning on/off the feature entirely -->
-    <string name="accessibility_daltonizer_master_switch_title">Use color correction</string>
+    <string name="accessibility_daltonizer_master_switch_title">Correct colors</string>
     <!-- Used in the Captions settings screen to control turning on/off the feature entirely -->
     <string name="accessibility_caption_master_switch_title">Show captions</string>
     <!-- Title for Caption preference settings screen for configuring font style. [CHAR LIMIT=NONE] -->
@@ -6161,6 +6173,11 @@
     <string name="admin_more_details">More details</string>
     <string name="notification_log_title">Notification log</string>
 
+    <string name="notification_history_title">Notification history</string>
+    <string name="notification_history_today">Today</string>
+    <string name="notification_history_snooze">Snoozed</string>
+    <string name="notification_history_dismiss">Recently dismissed</string>
+
     <!-- Category title for phone call's ringtone and vibration settings in the Sound Setting.
          [CHAR LIMIT=40] -->
     <string name="sound_category_call_ringtone_vibrate_title">Call ringtone &amp; vibrate</string>
@@ -9061,13 +9078,21 @@
     </plurals>
 
     <!-- Explanation that the app that will ALWAYS be launched to open web links to domains that it understands -->
-    <string name="app_link_open_always">Open in this app</string>
+    <string name="app_link_open_always">Allow app to open supported links</string>
 
     <!-- Explanation that the user will be asked whether to launch the app to open web links to domains that it understands -->
     <string name="app_link_open_ask">Ask every time</string>
 
     <!-- Explanation that the app that will NEVER be launched to open web links to domains that it understands -->
-    <string name="app_link_open_never">Don&#8217;t open in this app</string>
+    <string name="app_link_open_never">Don&#8217;t allow app to open links</string>
+
+    <plurals name="app_link_open_always_summary">
+        <item quantity="one">App claims to handle <xliff:g id="count">%d</xliff:g> link</item>
+        <item quantity="other">App claims to handle <xliff:g id="count">%d</xliff:g> links</item>
+    </plurals>
+
+    <!-- Footer of open supported links settings [CHAR LIMIT=NONE] -->
+    <string name="open_supported_links_footer">App claims to handle following links:</string>
 
     <!-- Title for Default Apps settings [CHAR LIMIT=30] -->
     <string name="default_apps_title">Default</string>
@@ -10797,13 +10822,15 @@
     <!-- Title for App Compatibility Changes dashboard where developers can configure per-app overrides for compatibility changes [CHAR LIMIT=50] -->
     <string name="platform_compat_dashboard_title">App Compatibility Changes</string>
     <!-- Summary for App Compatibility Changes dashboard [CHAR LIMIT=NONE] -->
-    <string name="platform_compat_dashboard_summary">Modify app compatibility change overrides</string>
+    <string name="platform_compat_dashboard_summary">Toggle app compatibility changes</string>
+    <!-- Summary for selected app [DO NOT TRANSLATE] -->
+    <string name="platform_compat_selected_app_summary" translatable="false"><xliff:g id="app_name" example="com.google.android.chrome">%1$s</xliff:g> targetSdkVersion <xliff:g id="number" example="29">%2$d</xliff:g></string>
     <!-- Title for default enabled app compat changes category [CHAR LIMIT=50] -->
     <string name="platform_compat_default_enabled_title">Default enabled changes</string>
     <!-- Title for default disabled app compat changes category [CHAR LIMIT=50] -->
     <string name="platform_compat_default_disabled_title">Default disabled changes</string>
-    <!-- Title for target SDK gated app compat changes category [CHAR LIMIT=50] -->
-    <string name="platform_compat_target_sdk_title">Enabled after SDK <xliff:g id="number" example="29">%d</xliff:g></string>
+    <!-- Title for target SDK gated app compat changes category (do not translate 'targetSdkVersion') [CHAR LIMIT=50] -->
+    <string name="platform_compat_target_sdk_title">Enabled for targetSdkVersion &gt; <xliff:g id="number" example="29">%d</xliff:g></string>
 
     <!-- Slices Strings -->
 
@@ -11301,14 +11328,6 @@
     <!-- Message for Network connection connecting progress Dialog. Try to connect to wifi ap.[CHAR LIMIT=40] -->
     <string name="network_connection_connecting_message">Connecting to device\u2026</string>
 
-    <!-- Summary for bluetooth devices count in Bluetooth devices slice. [CHAR LIMIT=NONE] -->
-    <plurals name="show_bluetooth_devices">
-        <item quantity="one"><xliff:g id="number_device_count">%1$d</xliff:g> device connected</item>
-        <item quantity="other"><xliff:g id="number_device_count">%1$d</xliff:g> devices connected</item>
-    </plurals>
-    <!-- Title for no bluetooth devices in Bluetooth devices slice. [CHAR LIMIT=NONE] -->
-    <string name="no_bluetooth_devices">No Bluetooth devices</string>
-
     <!-- Title for left bluetooth device. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_left_name">Left</string>
     <!-- Title for right bluetooth device. [CHAR LIMIT=NONE] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 05191b8..a4c82b2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -462,26 +462,6 @@
         <item name="rowStyle">@style/SliceRowStyle.Settings</item>
     </style>
 
-    <style name="TextAppearance.DeferredSetupCardTitle">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-        <item name="android:textSize">16sp</item>
-    </style>
-
-    <style name="TextAppearance.DeferredSetupCardSummary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
-    </style>
-
-    <style name="DeferredSetupCardButton" parent="android:Widget.DeviceDefault.Button.Colored">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
-        <item name="android:paddingBottom">@dimen/contextual_deferred_setup_card_button_padding_bottom</item>
-        <item name="android:paddingEnd">@dimen/contextual_deferred_setup_card_button_padding_end</item>
-        <item name="android:paddingStart">@dimen/contextual_deferred_setup_card_button_padding_start</item>
-        <item name="android:paddingTop">@dimen/contextual_deferred_setup_card_button_padding_top</item>
-        <item name="android:textAllCaps">false</item>
-        <item name="android:textSize">14sp</item>
-    </style>
-
     <style name="SliceViewStyle">
         <item name="rowStyle">@style/SliceRowStyle</item>
         <item name="android:background">?android:attr/colorBackgroundFloating</item>
diff --git a/res/xml/about_legal.xml b/res/xml/about_legal.xml
index 90b1985..6c59cac 100644
--- a/res/xml/about_legal.xml
+++ b/res/xml/about_legal.xml
@@ -59,6 +59,7 @@
         android:key="wallpaper_attributions"
         android:title="@string/wallpaper_attributions"
         android:summary="@string/wallpaper_attributions_values"
+        android:selectable="false"
         settings:controller="com.android.settings.deviceinfo.legal.WallpaperAttributionsPreferenceController" />
 
 </PreferenceScreen>
diff --git a/res/xml/accessibility_color_inversion_settings.xml b/res/xml/accessibility_color_inversion_settings.xml
index de1511f..9e31ff9 100644
--- a/res/xml/accessibility_color_inversion_settings.xml
+++ b/res/xml/accessibility_color_inversion_settings.xml
@@ -32,11 +32,17 @@
         settings:allowDividerBelow="true"
         settings:searchable="false" />
 
-    <com.android.settingslib.widget.FooterPreference
-        android:key="color_inversion_footer"
-        android:persistent="false"
-        android:selectable="false"
-        android:title="@string/accessibility_display_inversion_preference_subtitle"
-        settings:allowDividerAbove="true"
-        settings:searchable="false" />
+    <PreferenceCategory
+        android:key="color_inversion_footer_category">
+
+        <com.android.settingslib.widget.FooterPreference
+            android:key="color_inversion_footer"
+            android:persistent="false"
+            android:selectable="false"
+            settings:allowDividerAbove="false"
+            android:title="@string/accessibility_display_inversion_preference_subtitle"
+            settings:searchable="false" />
+
+    </PreferenceCategory>
+
 </PreferenceScreen>
diff --git a/res/xml/accessibility_daltonizer_settings.xml b/res/xml/accessibility_daltonizer_settings.xml
index 75386f5..976f912 100644
--- a/res/xml/accessibility_daltonizer_settings.xml
+++ b/res/xml/accessibility_daltonizer_settings.xml
@@ -26,31 +26,43 @@
         android:persistent="false"
         android:selectable="false"
         android:title="@string/summary_placeholder"
+        settings:allowDividerBelow="true"
         settings:searchable="false"/>
 
-    <com.android.settingslib.widget.RadioButtonPreference
-        android:key="daltonizer_mode_deuteranomaly"
-        android:persistent="false"
-        android:summary="@string/daltonizer_mode_deuteranomaly_summary"
-        android:title="@string/daltonizer_mode_deuteranomaly_title"
-        settings:allowDividerAbove="true" />
+    <PreferenceCategory
+        android:title="@string/daltonizer_type"
+        android:key="daltonizer_mode_category" >
 
-    <com.android.settingslib.widget.RadioButtonPreference
-        android:key="daltonizer_mode_protanomaly"
-        android:persistent="false"
-        android:summary="@string/daltonizer_mode_protanomaly_summary"
-        android:title="@string/daltonizer_mode_protanomaly_title" />
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="daltonizer_mode_deuteranomaly"
+            android:persistent="false"
+            android:summary="@string/daltonizer_mode_deuteranomaly_summary"
+            android:title="@string/daltonizer_mode_deuteranomaly_title" />
 
-    <com.android.settingslib.widget.RadioButtonPreference
-        android:key="daltonizer_mode_tritanomaly"
-        android:persistent="false"
-        android:summary="@string/daltonizer_mode_tritanomaly_summary"
-        android:title="@string/daltonizer_mode_tritanomaly_title" />
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="daltonizer_mode_protanomaly"
+            android:persistent="false"
+            android:summary="@string/daltonizer_mode_protanomaly_summary"
+            android:title="@string/daltonizer_mode_protanomaly_title" />
 
-    <com.android.settingslib.widget.FooterPreference
-        android:key="daltonizer_footer"
-        android:persistent="false"
-        android:selectable="false"
-        android:title="@string/accessibility_display_daltonizer_preference_subtitle"
-        settings:searchable="false" />
+        <com.android.settingslib.widget.RadioButtonPreference
+            android:key="daltonizer_mode_tritanomaly"
+            android:persistent="false"
+            android:summary="@string/daltonizer_mode_tritanomaly_summary"
+            android:title="@string/daltonizer_mode_tritanomaly_title" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="daltonizer_footer_category" >
+
+        <com.android.settingslib.widget.FooterPreference
+            android:key="daltonizer_footer"
+            android:persistent="false"
+            android:selectable="false"
+            settings:allowDividerAbove="false"
+            android:title="@string/accessibility_display_daltonizer_preference_subtitle"
+            settings:searchable="false" />
+
+    </PreferenceCategory>
 </PreferenceScreen>
diff --git a/res/xml/dark_mode_settings.xml b/res/xml/dark_mode_settings.xml
index 9247a0c..cf91186 100644
--- a/res/xml/dark_mode_settings.xml
+++ b/res/xml/dark_mode_settings.xml
@@ -30,6 +30,16 @@
         settings:controller="com.android.settings.display.darkmode.DarkModeScheduleSelectorController"
         settings:keywords="@string/keywords_dark_ui_mode"/>
 
+    <Preference
+        android:key="dark_theme_start_time"
+        android:title="@string/night_display_start_time_title"
+        settings:searchable="false"/>
+
+    <Preference
+        android:key="dark_theme_end_time"
+        android:title="@string/night_display_end_time_title"
+        settings:searchable="false"/>
+
     <com.android.settingslib.widget.LayoutPreference
         android:key="dark_ui_activated"
         android:title="@string/dark_ui_title"
diff --git a/res/xml/installed_app_launch_settings.xml b/res/xml/installed_app_launch_settings.xml
index a2a0ca4..6cf1955 100644
--- a/res/xml/installed_app_launch_settings.xml
+++ b/res/xml/installed_app_launch_settings.xml
@@ -21,16 +21,16 @@
     <PreferenceCategory android:key="app_launch_domain_links"
                         android:title="@string/app_launch_domain_links_title">
 
-        <DropDownPreference
-                android:key="app_link_state"
-                android:summary="%s"
-                android:title="@string/app_launch_open_domain_urls_title" />
+        <Preference
+            android:key="app_link_state"
+            android:summary="@string/summary_placeholder"
+            android:title="@string/app_launch_open_domain_urls_title"/>
 
         <com.android.settings.applications.AppDomainsPreference
-                android:key="app_launch_supported_domain_urls"
-                android:title="@string/app_launch_supported_domain_urls_title"
-                android:dependency="app_link_state"
-                />
+            android:key="app_launch_supported_domain_urls"
+            android:title="@string/app_launch_supported_domain_urls_title"
+            android:dependency="app_link_state"
+        />
 
     </PreferenceCategory>
 
@@ -38,7 +38,7 @@
                         android:title="@string/app_launch_other_defaults_title">
 
         <com.android.settings.applications.ClearDefaultsPreference
-                android:key="app_launch_clear_defaults" />
+            android:key="app_launch_clear_defaults"/>
 
     </PreferenceCategory>
 
diff --git a/res/xml/open_supported_links.xml b/res/xml/open_supported_links.xml
new file mode 100644
index 0000000..0f6e2ca
--- /dev/null
+++ b/res/xml/open_supported_links.xml
@@ -0,0 +1,40 @@
+<?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/app_launch_open_domain_urls_title">
+
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="app_header_view"
+        android:layout="@layout/settings_entity_header"
+        android:selectable="false"
+        settings:controller="com.android.settings.applications.AppHeaderPreferenceController"
+        settings:allowDividerBelow="true"/>
+
+    <PreferenceCategory
+        android:key="app_launch_allow_app_to_open_supported_links"
+        android:title="@string/app_link_open_always"
+        settings:controller="com.android.settings.applications.AppOpenSupportedLinksPreferenceController">
+    </PreferenceCategory>
+
+    <com.android.settingslib.widget.FooterPreference
+        android:key="supported_links_footer"
+        android:title="@string/open_supported_links_footer"
+        android:selectable="false"
+        settings:searchable="false"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SettingsDumpService.java b/src/com/android/settings/SettingsDumpService.java
index 487ccf5..2b6c7d8 100644
--- a/src/com/android/settings/SettingsDumpService.java
+++ b/src/com/android/settings/SettingsDumpService.java
@@ -102,11 +102,11 @@
         JSONObject obj = new JSONObject();
         DataUsageController controller = new DataUsageController(this);
         ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
-        SubscriptionManager manager = SubscriptionManager.from(this);
+        SubscriptionManager manager = this.getSystemService(SubscriptionManager.class);
         TelephonyManager telephonyManager = this.getSystemService(TelephonyManager.class);
         if (connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
             JSONArray array = new JSONArray();
-            for (SubscriptionInfo info : manager.getAllSubscriptionInfoList()) {
+            for (SubscriptionInfo info : manager.getAvailableSubscriptionInfoList()) {
                 telephonyManager = telephonyManager
                         .createForSubscriptionId(info.getSubscriptionId());
                 NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index 7136172..c96013f 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -171,7 +171,7 @@
 
     private static void initMagnifyFullScreen(Context context, View view) {
         final View dialogView = view.findViewById(R.id.magnify_full_screen);
-        final String title = context.getString(
+        final CharSequence title = context.getText(
                 R.string.accessibility_magnification_area_settings_full_screen);
         // TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
         setupShortcutWidget(dialogView, title, R.drawable.accessibility_magnification_full_screen);
@@ -179,7 +179,7 @@
 
     private static void initMagnifyWindowScreen(Context context, View view) {
         final View dialogView = view.findViewById(R.id.magnify_window_screen);
-        final String title = context.getString(
+        final CharSequence title = context.getText(
                 R.string.accessibility_magnification_area_settings_window_screen);
         // TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
         setupShortcutWidget(dialogView, title,
@@ -214,9 +214,9 @@
 
     private static void initHardwareShortcut(Context context, View view) {
         final View dialogView = view.findViewById(R.id.hardware_shortcut);
-        final String title = context.getString(
+        final CharSequence title = context.getText(
                 R.string.accessibility_shortcut_edit_dialog_title_hardware);
-        final String summary = context.getString(
+        final CharSequence summary = context.getText(
                 R.string.accessibility_shortcut_edit_dialog_summary_hardware);
         setupShortcutWidget(dialogView, title, summary,
                 R.drawable.accessibility_shortcut_type_hardware);
@@ -225,9 +225,9 @@
 
     private static void initMagnifyShortcut(Context context, View view) {
         final View dialogView = view.findViewById(R.id.triple_tap_shortcut);
-        final String title = context.getString(
+        final CharSequence title = context.getText(
                 R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
-        final String summary = context.getString(
+        final CharSequence summary = context.getText(
                 R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
         setupShortcutWidget(dialogView, title, summary,
                 R.drawable.accessibility_shortcut_type_triple_tap);
@@ -244,23 +244,34 @@
     }
 
     private static CharSequence retrieveTitle(Context context) {
-        return context.getString(AccessibilityUtil.isGestureNavigateEnabled(context)
-                ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
-                : R.string.accessibility_shortcut_edit_dialog_title_software);
+        int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+        }
+        return context.getText(resId);
     }
 
     private static CharSequence retrieveSummary(Context context, int lineHeight) {
-        return AccessibilityUtil.isGestureNavigateEnabled(context)
-                ? context.getString(
-                R.string.accessibility_shortcut_edit_dialog_summary_software_gesture)
-                : getSummaryStringWithIcon(context, lineHeight);
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
+            return context.getText(resId);
+        }
+        return getSummaryStringWithIcon(context, lineHeight);
     }
 
     private static int retrieveImageResId(Context context) {
-        return AccessibilityUtil.isGestureNavigateEnabled(context)
-                ? R.drawable.accessibility_shortcut_type_software_gesture
-                : R.drawable.accessibility_shortcut_type_software;
         // TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
+        int resId = R.drawable.accessibility_shortcut_type_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
+                    : R.drawable.accessibility_shortcut_type_software_gesture;
+        }
+        return resId;
     }
 
     private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 5e6eced..0c8644f 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -27,7 +27,6 @@
 import android.view.TextureView;
 import android.view.View;
 import android.view.Window;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
@@ -117,13 +116,13 @@
                         R.id.gesture_tutorial_video);
                 final TextView gestureTutorialMessage = content.findViewById(
                         R.id.gesture_tutorial_message);
-                VideoPlayer.create(context, isTouchExploreOn(context)
+                VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
                                 ? R.raw.illustration_accessibility_gesture_three_finger
                                 : R.raw.illustration_accessibility_gesture_two_finger,
                         gestureTutorialVideo);
-                gestureTutorialMessage.setText(isTouchExploreOn(context)
-                        ? R.string.accessibility_tutorial_dialog_message_gesture_with_talkback
-                        : R.string.accessibility_tutorial_dialog_message_gesture_without_talkback);
+                gestureTutorialMessage.setText(AccessibilityUtil.isTouchExploreEnabled(context)
+                        ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
+                        : R.string.accessibility_tutorial_dialog_message_gesture);
                 break;
             case DialogType.GESTURE_NAVIGATION_SETTINGS:
                 content = inflater.inflate(
@@ -132,14 +131,14 @@
                         R.id.gesture_tutorial_video);
                 final TextView gestureSettingsTutorialMessage = content.findViewById(
                         R.id.gesture_tutorial_message);
-                VideoPlayer.create(context, isTouchExploreOn(context)
+                VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
                                 ? R.raw.illustration_accessibility_gesture_three_finger
                                 : R.raw.illustration_accessibility_gesture_two_finger,
                         gestureSettingsTutorialVideo);
-                gestureSettingsTutorialMessage.setText(isTouchExploreOn(context)
-                        ?
-                        R.string.accessibility_tutorial_dialog_message_gesture_settings_with_talkback
-                        : R.string.accessibility_tutorial_dialog_message_gesture_settings_without_talkback);
+                final int stringResId = AccessibilityUtil.isTouchExploreEnabled(context)
+                        ? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
+                        : R.string.accessibility_tutorial_dialog_message_gesture_settings;
+                gestureSettingsTutorialMessage.setText(stringResId);
                 break;
         }
 
@@ -203,9 +202,4 @@
         typedArray.recycle();
         return colorResId;
     }
-
-    private static boolean isTouchExploreOn(Context context) {
-        return ((AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))
-                .isTouchExplorationEnabled();
-    }
 }
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index fefb406..8da6fbb 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -24,6 +24,7 @@
 import android.os.Build;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -139,6 +140,12 @@
                 == NAV_BAR_MODE_GESTURAL;
     }
 
+    /** Determines if a touch explore is being used. */
+    public static boolean isTouchExploreEnabled(Context context) {
+        final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+        return am.isTouchExplorationEnabled();
+    }
+
     /**
      * Gets the corresponding fragment type of a given accessibility service.
      *
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index daba21c..87c1bf8 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -35,12 +35,16 @@
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 import android.widget.CheckBox;
 
+import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -66,7 +70,7 @@
 
     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
     private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
-    private ShortcutPreference mShortcutPreference;
+    private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private int mUserShortcutType = UserShortcutType.DEFAULT;
     // Used to restore the edit dialog status.
     private int mUserShortcutTypeCache = UserShortcutType.DEFAULT;
@@ -115,14 +119,25 @@
     }
 
     @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+            removeDialog(DialogType.EDIT_SHORTCUT);
+            mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+        };
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
         // Restore the user shortcut type.
         if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
             mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
                     UserShortcutType.DEFAULT);
         }
         initShortcutPreference();
+
+        super.onViewCreated(view, savedInstanceState);
     }
 
     @Override
@@ -134,7 +149,12 @@
     @Override
     public void onResume() {
         super.onResume();
+
         mSettingsContentObserver.register(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         updateSwitchBarToggleSwitch();
         updateShortcutPreferenceData();
         updateShortcutPreference();
@@ -143,6 +163,10 @@
     @Override
     public void onPause() {
         mSettingsContentObserver.unregister(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         super.onPause();
     }
 
@@ -284,10 +308,13 @@
 
     private String getShortcutTypeSummary(Context context) {
         final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
-        final CharSequence softwareTitle =
-                context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
-                ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
-                : R.string.accessibility_shortcut_edit_dialog_title_software);
+        int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+        }
+        final CharSequence softwareTitle = context.getText(resId);
 
         List<CharSequence> list = new ArrayList<>();
         if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -372,6 +399,15 @@
         }
     }
 
+    @Override
+    protected void updateFooterTitle(PreferenceCategory category) {
+        final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
+        final String titleText = (info == null) ? "" :
+                getString(R.string.accessibility_footer_title,
+                        info.getResolveInfo().loadLabel(getPackageManager()));
+        category.setTitle(titleText);
+    }
+
     private void initShortcutPreference() {
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         mShortcutPreference = new ShortcutPreference(
@@ -380,9 +416,6 @@
         mShortcutPreference.setKey(getShortcutPreferenceKey());
         mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
         mShortcutPreference.setOnClickListener(this);
-        // Put the shortcutPreference before settingsPreference.
-        mShortcutPreference.setOrder(-1);
-        preferenceScreen.addPreference(mShortcutPreference);
     }
 
     private void updateShortcutPreference() {
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index ca32239..80fd92f 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -31,11 +31,14 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 import android.widget.CheckBox;
 import android.widget.Switch;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -58,11 +61,12 @@
 
     private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
     private static final String PREVIEW_PREFERENCE_KEY = "color_inversion_preview";
+    private static final String CATEGORY_FOOTER_KEY = "color_inversion_footer_category";
     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
     private static final int DIALOG_ID_EDIT_SHORTCUT = 1;
     private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
     private final Handler mHandler = new Handler();
-    private ShortcutPreference mShortcutPreference;
+    private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private SettingsContentObserver mSettingsContentObserver;
     private int mUserShortcutType = UserShortcutType.DEFAULT;
     // Used to restore the edit dialog status.
@@ -71,18 +75,6 @@
     private CheckBox mHardwareTypeCheckBox;
 
     @Override
-    public void onStart() {
-        super.onStart();
-        mSettingsContentObserver.register(getContentResolver());
-    }
-
-    @Override
-    public void onStop() {
-        mSettingsContentObserver.unregister(getContentResolver());
-        super.onStop();
-    }
-
-    @Override
     public int getMetricsCategory() {
         return SettingsEnums.ACCESSIBILITY_COLOR_INVERSION_SETTINGS;
     }
@@ -105,10 +97,15 @@
 
     @Override
     protected void updateSwitchBarText(SwitchBar switchBar) {
-        final String switchBarText = getString(
-                R.string.accessibility_display_inversion_switch_title,
-                getString(R.string.accessibility_display_inversion_switch_title));
-        switchBar.setSwitchBarText(switchBarText, switchBarText);
+        switchBar.setSwitchBarText(R.string.accessibility_display_inversion_switch_title,
+                R.string.accessibility_display_inversion_switch_title);
+    }
+
+    @Override
+    protected void updateFooterTitle(PreferenceCategory category) {
+        final String titleText = getString(R.string.accessibility_footer_title,
+                getString(R.string.accessibility_display_inversion_preference_title));
+        category.setTitle(titleText);
     }
 
     @Override
@@ -127,13 +124,6 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        // Restore the user shortcut type.
-        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
-            mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
-                    UserShortcutType.DEFAULT);
-        }
-        initShortcutPreference();
-
         final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
         enableServiceFeatureKeys.add(ENABLED);
         mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@@ -144,10 +134,34 @@
                                 == State.ON);
             }
         };
+
+        mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+            removeDialog(DIALOG_ID_EDIT_SHORTCUT);
+            mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+        };
         return super.onCreateView(inflater, container, savedInstanceState);
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        // Restore the user shortcut type.
+        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
+            mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
+                    UserShortcutType.DEFAULT);
+        }
+        initShortcutPreference();
+
+        super.onViewCreated(view, savedInstanceState);
+
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        preferenceScreen.setOrderingAsAdded(false);
+        final PreferenceCategory footerCategory = preferenceScreen.findPreference(
+                CATEGORY_FOOTER_KEY);
+        updateFooterTitle(footerCategory);
+        footerCategory.setOrder(Integer.MAX_VALUE);
+    }
+
+    @Override
     public void onSaveInstanceState(Bundle outState) {
         outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypeCache);
         super.onSaveInstanceState(outState);
@@ -156,14 +170,30 @@
     @Override
     public void onResume() {
         super.onResume();
+
+        mSettingsContentObserver.register(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         updateShortcutPreferenceData();
         updateShortcutPreference();
     }
 
     @Override
+    public void onPause() {
+        mSettingsContentObserver.unregister(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
+        super.onPause();
+    }
+
+    @Override
     public Dialog onCreateDialog(int dialogId) {
         if (dialogId == DIALOG_ID_EDIT_SHORTCUT) {
-            final CharSequence dialogTitle = getActivity().getString(
+            final CharSequence dialogTitle = getActivity().getText(
                     R.string.accessibility_shortcut_edit_dialog_title_daltonizer);
             final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
                     getActivity(),
@@ -239,10 +269,13 @@
 
     private String getShortcutTypeSummary(Context context) {
         final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
-        final CharSequence softwareTitle =
-                context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
-                        ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
-                        : R.string.accessibility_shortcut_edit_dialog_title_software);
+        int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+        }
+        final CharSequence softwareTitle = context.getText(resId);
 
         List<CharSequence> list = new ArrayList<>();
         if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -318,9 +351,6 @@
         mShortcutPreference.setKey(getShortcutPreferenceKey());
         mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
         mShortcutPreference.setOnClickListener(this);
-        // Put the shortcutPreference before previewPreference.
-        mShortcutPreference.setOrder(previewPreference.getOrder() - 1);
-        preferenceScreen.addPreference(mShortcutPreference);
     }
 
     private void updateShortcutPreference() {
diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
index 3016f6a..ca95241 100644
--- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@@ -32,11 +32,14 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 import android.widget.CheckBox;
 import android.widget.Switch;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -47,7 +50,6 @@
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.RadioButtonPreference;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -61,13 +63,13 @@
         SwitchBar.OnSwitchChangeListener, ShortcutPreference.OnClickListener {
 
     private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED;
-    private static final String PREFERENCE_KEY = "daltonizer_mode_deuteranomaly";
+    private static final String CATEGORY_FOOTER_KEY = "daltonizer_footer_category";
     private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
     private static final int DIALOG_ID_EDIT_SHORTCUT = 1;
     private static final List<AbstractPreferenceController> sControllers = new ArrayList<>();
     private final Handler mHandler = new Handler();
-    private ShortcutPreference mShortcutPreference;
+    private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private SettingsContentObserver mSettingsContentObserver;
     private int mUserShortcutType = UserShortcutType.DEFAULT;
     // Used to restore the edit dialog status.
@@ -100,13 +102,6 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        // Restore the user shortcut type.
-        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
-            mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
-                    UserShortcutType.DEFAULT);
-        }
-        initShortcutPreference();
-
         final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
         enableServiceFeatureKeys.add(ENABLED);
         mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@@ -117,10 +112,34 @@
                                 == State.ON);
             }
         };
+
+        mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+            removeDialog(DIALOG_ID_EDIT_SHORTCUT);
+            mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+        };
         return super.onCreateView(inflater, container, savedInstanceState);
     }
 
     @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        // Restore the user shortcut type.
+        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
+            mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
+                    UserShortcutType.DEFAULT);
+        }
+        initShortcutPreference();
+
+        super.onViewCreated(view, savedInstanceState);
+
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        preferenceScreen.setOrderingAsAdded(false);
+        final PreferenceCategory footerCategory = preferenceScreen.findPreference(
+                CATEGORY_FOOTER_KEY);
+        updateFooterTitle(footerCategory);
+        footerCategory.setOrder(Integer.MAX_VALUE);
+    }
+
+    @Override
     public void onSaveInstanceState(Bundle outState) {
         outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypeCache);
         super.onSaveInstanceState(outState);
@@ -129,6 +148,12 @@
     @Override
     public void onResume() {
         super.onResume();
+
+        mSettingsContentObserver.register(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         for (AbstractPreferenceController controller :
                 buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
             ((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(this);
@@ -141,17 +166,23 @@
 
     @Override
     public void onPause() {
-        super.onPause();
+        mSettingsContentObserver.unregister(getContentResolver());
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         for (AbstractPreferenceController controller :
                 buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
             ((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(null);
         }
+
+        super.onPause();
     }
 
     @Override
     public Dialog onCreateDialog(int dialogId) {
         if (dialogId == DIALOG_ID_EDIT_SHORTCUT) {
-            final CharSequence dialogTitle = getActivity().getString(
+            final CharSequence dialogTitle = getActivity().getText(
                     R.string.accessibility_shortcut_edit_dialog_title_daltonizer);
             final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
                     getActivity(),
@@ -227,10 +258,13 @@
 
     private String getShortcutTypeSummary(Context context) {
         final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
-        final CharSequence softwareTitle =
-                context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
-                        ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
-                        : R.string.accessibility_shortcut_edit_dialog_title_software);
+        int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+        }
+        final CharSequence softwareTitle = context.getText(resId);
 
         List<CharSequence> list = new ArrayList<>();
         if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -278,18 +312,6 @@
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
-        mSettingsContentObserver.register(getContentResolver());
-    }
-
-    @Override
-    public void onStop() {
-        mSettingsContentObserver.unregister(getContentResolver());
-        super.onStop();
-    }
-
-    @Override
     public int getMetricsCategory() {
         return SettingsEnums.ACCESSIBILITY_TOGGLE_DALTONIZER;
     }
@@ -325,9 +347,15 @@
 
     @Override
     protected void updateSwitchBarText(SwitchBar switchBar) {
-        final String switchBarText = getString(R.string.accessibility_service_master_switch_title,
-                getString(R.string.accessibility_display_daltonizer_preference_title));
-        switchBar.setSwitchBarText(switchBarText, switchBarText);
+        switchBar.setSwitchBarText(R.string.accessibility_daltonizer_master_switch_title,
+                R.string.accessibility_daltonizer_master_switch_title);
+    }
+
+    @Override
+    protected void updateFooterTitle(PreferenceCategory category) {
+        final String titleText = getString(R.string.accessibility_footer_title,
+                        getString(R.string.accessibility_display_daltonizer_preference_title));
+        category.setTitle(titleText);
     }
 
     @Override
@@ -370,10 +398,6 @@
         mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
         mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
         mShortcutPreference.setOnClickListener(this);
-        final RadioButtonPreference radioButtonPreference = findPreference(PREFERENCE_KEY);
-        // Put the shortcutPreference before radioButtonPreference.
-        mShortcutPreference.setOrder(radioButtonPreference.getOrder() - 1);
-        preferenceScreen.addPreference(mShortcutPreference);
     }
 
     private void updateShortcutPreferenceData() {
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index fe2449c..28c1e1c 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -29,6 +29,7 @@
 import android.widget.ImageView;
 
 import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -45,7 +46,8 @@
 
     protected SwitchBar mSwitchBar;
     protected ToggleSwitch mToggleSwitch;
-
+    protected ShortcutPreference mShortcutPreference;
+    protected Preference mSettingsPreference;
     protected String mPreferenceKey;
 
     protected CharSequence mSettingsTitle;
@@ -98,16 +100,6 @@
         updateSwitchBarText(mSwitchBar);
 
         PreferenceScreen preferenceScreen = getPreferenceScreen();
-
-        // Show the "Settings" menu as if it were a preference screen
-        if (mSettingsTitle != null && mSettingsIntent != null) {
-            Preference settingsPref = new Preference(preferenceScreen.getContext());
-            settingsPref.setTitle(mSettingsTitle);
-            settingsPref.setIconSpaceReserved(true);
-            settingsPref.setIntent(mSettingsIntent);
-            preferenceScreen.addPreference(settingsPref);
-        }
-
         if (mImageUri != null) {
             final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
                     preferenceScreen.getContext());
@@ -116,27 +108,55 @@
             preferenceScreen.addPreference(animatedImagePreference);
         }
 
-        if (mStaticDescription != null) {
-            final StaticTextPreference staticTextPreference = new StaticTextPreference(
-                    preferenceScreen.getContext());
-            staticTextPreference.setSelectable(/* selectable= */ false);
-            staticTextPreference.setSummary(mStaticDescription);
-            preferenceScreen.addPreference(staticTextPreference);
+        // Show the "Settings" menu as if it were a preference screen.
+        if (mSettingsTitle != null && mSettingsIntent != null) {
+            mSettingsPreference = new Preference(preferenceScreen.getContext());
+            mSettingsPreference.setTitle(mSettingsTitle);
+            mSettingsPreference.setIconSpaceReserved(true);
+            mSettingsPreference.setIntent(mSettingsIntent);
         }
 
-        if (mHtmlDescription != null) {
-            // For accessibility service, avoid malicious links made by third party developer
-            final List<String> unsupportedTagList = new ArrayList<>();
-            unsupportedTagList.add(ANCHOR_TAG);
+        if (mSettingsPreference != null || mShortcutPreference != null) {
+            final PreferenceCategory category = new PreferenceCategory(getPrefContext());
+            category.setTitle(R.string.accessibility_screen_option);
+            preferenceScreen.addPreference(category);
 
-            final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(
-                    preferenceScreen.getContext());
-            htmlTextPreference.setSelectable(/* selectable= */ false);
-            htmlTextPreference.setSummary(mHtmlDescription);
-            htmlTextPreference.setImageGetter(mImageGetter);
-            htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
-            htmlTextPreference.setDividerAllowedAbove(true);
-            preferenceScreen.addPreference(htmlTextPreference);
+            if (mShortcutPreference != null) {
+                category.addPreference(mShortcutPreference);
+            }
+
+            if (mSettingsPreference != null) {
+                category.addPreference(mSettingsPreference);
+            }
+        }
+
+        if (mStaticDescription != null ||  mHtmlDescription != null) {
+            final PreferenceCategory footerCategory = new PreferenceCategory(getPrefContext());
+            updateFooterTitle(footerCategory);
+            preferenceScreen.addPreference(footerCategory);
+
+            if (mStaticDescription != null) {
+                final StaticTextPreference staticTextPreference = new StaticTextPreference(
+                        preferenceScreen.getContext());
+                staticTextPreference.setSummary(mStaticDescription);
+                staticTextPreference.setSelectable(/* selectable= */ false);
+                footerCategory.addPreference(staticTextPreference);
+            }
+
+            if (mHtmlDescription != null) {
+                // For accessibility service, avoid malicious links made by third party developer.
+                final List<String> unsupportedTagList = new ArrayList<>();
+                unsupportedTagList.add(ANCHOR_TAG);
+
+                final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(
+                        preferenceScreen.getContext());
+                htmlTextPreference.setSummary(mHtmlDescription);
+                htmlTextPreference.setImageGetter(mImageGetter);
+                htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
+                htmlTextPreference.setDividerAllowedAbove(true);
+                htmlTextPreference.setSelectable(/* selectable= */ false);
+                footerCategory.addPreference(htmlTextPreference);
+            }
         }
     }
 
@@ -149,16 +169,23 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
-
         removeActionBarToggleSwitch();
     }
 
     protected void updateSwitchBarText(SwitchBar switchBar) {
-        // Implement this to provide meaningful text in switch bar
+        // Implement this to provide meaningful text in switch bar.
         switchBar.setSwitchBarText(R.string.accessibility_service_master_switch_title,
                 R.string.accessibility_service_master_switch_title);
     }
 
+    protected void updateFooterTitle(PreferenceCategory category) {
+        // Implement this to provide meaningful text in the footer.
+        if (category != null) {
+            category.setTitle(getString(R.string.accessibility_footer_title,
+                    mComponentName.getPackageName()));
+        }
+    }
+
     protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
 
     protected void onInstallSwitchBarToggleSwitch() {
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 3060ddb..8e4da9d 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -37,6 +37,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
 import android.widget.CheckBox;
 import android.widget.ImageView;
 import android.widget.RelativeLayout.LayoutParams;
@@ -67,7 +69,7 @@
     private static final String SETTINGS_KEY = "screen_magnification_settings";
     private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
     private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
-    private ShortcutPreference mShortcutPreference;
+    private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
     private int mUserShortcutType = UserShortcutType.DEFAULT;
     // Used to restore the edit dialog status.
     private int mUserShortcutTypeCache = UserShortcutType.DEFAULT;
@@ -84,6 +86,7 @@
 
     protected Preference mConfigWarningPreference;
     protected VideoPreference mVideoPreference;
+
     protected class VideoPreference extends Preference {
         private ImageView mVideoBackgroundView;
         private OnGlobalLayoutListener mLayoutListener;
@@ -169,50 +172,44 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
+        mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+            removeDialog(DialogType.EDIT_SHORTCUT);
+            mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+        };
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
 
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        final PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen();
         mVideoPreference = new VideoPreference(getPrefContext());
         mVideoPreference.setSelectable(false);
         mVideoPreference.setPersistent(false);
         mVideoPreference.setLayoutResource(R.layout.magnification_video_preference);
+        preferenceScreen.addPreference(mVideoPreference);
 
-        final PreferenceCategory optionCategory = new PreferenceCategory(getPrefContext());
-        optionCategory.setTitle(R.string.accessibility_screen_option);
-
-        // Restore the user shortcut type.
-        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
-            mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
-                    UserShortcutType.DEFAULT);
-        }
         initShortcutPreference();
 
-        final Preference settingsPreference = new Preference(getPrefContext());
-        settingsPreference.setTitle(R.string.accessibility_magnification_service_settings_title);
-        settingsPreference.setKey(SETTINGS_KEY);
-        settingsPreference.setFragment(MagnificationSettingsFragment.class.getName());
-        settingsPreference.setPersistent(false);
+        mSettingsPreference = new Preference(getPrefContext());
+        mSettingsPreference.setTitle(R.string.accessibility_magnification_service_settings_title);
+        mSettingsPreference.setKey(SETTINGS_KEY);
+        mSettingsPreference.setFragment(MagnificationSettingsFragment.class.getName());
+        mSettingsPreference.setPersistent(false);
 
-        final PreferenceCategory aboutCategory = new PreferenceCategory(getPrefContext());
-        aboutCategory.setTitle(R.string.accessibility_screen_magnification_about);
+        super.onViewCreated(view, savedInstanceState);
 
         mConfigWarningPreference = new Preference(getPrefContext());
         mConfigWarningPreference.setSelectable(false);
         mConfigWarningPreference.setPersistent(false);
         mConfigWarningPreference.setVisible(false);
-        mConfigWarningPreference.setIcon(R.drawable.ic_warning_24dp);
+        preferenceScreen.addPreference(mConfigWarningPreference);
+    }
 
-        final PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen();
-        preferenceScreen.setOrderingAsAdded(false);
-        mVideoPreference.setOrder(0);
-        optionCategory.setOrder(1);
-        aboutCategory.setOrder(2);
-        preferenceScreen.addPreference(mVideoPreference);
-        preferenceScreen.addPreference(optionCategory);
-        optionCategory.addPreference(mShortcutPreference);
-        optionCategory.addPreference(settingsPreference);
-        preferenceScreen.addPreference(aboutCategory);
-        aboutCategory.addPreference(mConfigWarningPreference);
-
-        return super.onCreateView(inflater, container, savedInstanceState);
+    @Override
+    protected void updateFooterTitle(PreferenceCategory category) {
+        final String titleText = getString(R.string.accessibility_footer_title,
+                getString(R.string.accessibility_screen_magnification_title));
+        category.setTitle(titleText);
     }
 
     @Override
@@ -225,6 +222,10 @@
     public void onResume() {
         super.onResume();
 
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
         VideoView videoView = (VideoView) getView().findViewById(R.id.video);
         if (videoView != null) {
             videoView.start();
@@ -236,6 +237,15 @@
     }
 
     @Override
+    public void onPause() {
+        final AccessibilityManager am = getPrefContext().getSystemService(
+                AccessibilityManager.class);
+        am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
+        super.onPause();
+    }
+
+    @Override
     public Dialog onCreateDialog(int dialogId) {
         switch (dialogId) {
             case DialogType.GESTURE_NAVIGATION_TUTORIAL:
@@ -245,7 +255,7 @@
                 return AccessibilityGestureNavigationTutorial
                         .showAccessibilityButtonTutorialDialog(getActivity());
             case DialogType.EDIT_SHORTCUT:
-                final CharSequence dialogTitle = getActivity().getString(
+                final CharSequence dialogTitle = getActivity().getText(
                         R.string.accessibility_shortcut_edit_dialog_title_magnification);
                 final AlertDialog dialog =
                         AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
@@ -342,10 +352,13 @@
 
     private String getShortcutTypeSummary(Context context) {
         final int shortcutType = getUserShortcutType(context, UserShortcutType.DEFAULT);
-        final CharSequence softwareTitle =
-                context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
-                        ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
-                        : R.string.accessibility_shortcut_edit_dialog_title_software);
+        int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+        if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+            resId = AccessibilityUtil.isTouchExploreEnabled(context)
+                    ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+                    : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+        }
+        final CharSequence softwareTitle = context.getText(resId);
 
         List<CharSequence> list = new ArrayList<>();
         if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
index 5fe62a7..ffa0c41 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
@@ -18,6 +18,7 @@
 
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
+import android.view.View;
 
 public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
         extends ToggleScreenMagnificationPreferenceFragment {
@@ -41,4 +42,12 @@
         }
         super.onStop();
     }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        // Hide the setting from the vision settings.
+        mSettingsPreference.setVisible(false);
+    }
 }
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index be9e8d4..5f01e4b 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -20,7 +20,7 @@
 import android.os.Bundle;
 
 public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
-        extends ToggleAccessibilityServicePreferenceFragment {
+        extends InvisibleToggleAccessibilityServicePreferenceFragment {
 
     private boolean mToggleSwitchWasInitiallyChecked;
 
diff --git a/src/com/android/settings/applications/AppHeaderPreferenceController.java b/src/com/android/settings/applications/AppHeaderPreferenceController.java
new file mode 100644
index 0000000..8a77d6f
--- /dev/null
+++ b/src/com/android/settings/applications/AppHeaderPreferenceController.java
@@ -0,0 +1,107 @@
+/*
+ * 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.applications;
+
+import static com.android.settings.widget.EntityHeaderController.ActionType;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.IconDrawableFactory;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ *  The header controller displays on the top of the page.
+ */
+public class AppHeaderPreferenceController extends BasePreferenceController implements
+        LifecycleObserver, OnResume {
+    private DashboardFragment mParent;
+    private PackageInfo mPackageInfo;
+    private Lifecycle mLifecycle;
+    private LayoutPreference mHeaderPreference;
+
+    public AppHeaderPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    /**
+     * @param fragment set the parent fragment.
+     * @return return controller-self.
+     */
+    public AppHeaderPreferenceController setParentFragment(DashboardFragment fragment) {
+        mParent = fragment;
+        return this;
+    }
+
+    /**
+     * @param packageInfo set the {@link PackageInfo}.
+     * @return return controller-self.
+     */
+    public AppHeaderPreferenceController setPackageInfo(PackageInfo packageInfo) {
+        mPackageInfo = packageInfo;
+        return this;
+    }
+
+    /**
+     * @param lifeCycle set the {@link Lifecycle}.
+     * @return return controller-self.
+     */
+    public AppHeaderPreferenceController setLifeCycle(Lifecycle lifeCycle) {
+        mLifecycle = lifeCycle;
+        return this;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mHeaderPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void onResume() {
+        final Activity activity = mParent.getActivity();
+        final PackageManager packageManager = activity.getPackageManager();
+        EntityHeaderController
+                .newInstance(activity, mParent, mHeaderPreference.findViewById(R.id.entity_header))
+                .setRecyclerView(mParent.getListView(), mLifecycle)
+                .setIcon(IconDrawableFactory.newInstance(activity).getBadgedIcon(
+                        mPackageInfo.applicationInfo))
+                .setLabel(mPackageInfo.applicationInfo.loadLabel(packageManager))
+                .setSummary(mPackageInfo)
+                .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+                .setPackageName(mPackageInfo.packageName)
+                .setUid(mPackageInfo.applicationInfo.uid)
+                .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
+                .done(mParent.getActivity(), true /* rebindActions */);
+    }
+}
diff --git a/src/com/android/settings/applications/AppLaunchSettings.java b/src/com/android/settings/applications/AppLaunchSettings.java
index c61f5d1..17d8e67 100644
--- a/src/com/android/settings/applications/AppLaunchSettings.java
+++ b/src/com/android/settings/applications/AppLaunchSettings.java
@@ -17,14 +17,11 @@
 package com.android.settings.applications;
 
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 
 import android.app.settings.SettingsEnums;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
@@ -36,24 +33,27 @@
 import android.view.View.OnClickListener;
 
 import androidx.appcompat.app.AlertDialog;
-import androidx.preference.DropDownPreference;
 import androidx.preference.Preference;
-import androidx.preference.Preference.OnPreferenceChangeListener;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
 
 import java.util.List;
 
 public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
         Preference.OnPreferenceChangeListener {
     private static final String TAG = "AppLaunchSettings";
-
     private static final String KEY_APP_LINK_STATE = "app_link_state";
     private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
     private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
+    private static final String FRAGMENT_OPEN_SUPPORTED_LINKS =
+            "com.android.settings.applications.OpenSupportedLinks";
+
+    public static final String KEY_PACKAGE_INFO = "pkg_info";
 
     private static final Intent sBrowserIntent;
+
     static {
         sBrowserIntent = new Intent()
                 .setAction(Intent.ACTION_VIEW)
@@ -65,7 +65,7 @@
 
     private boolean mIsBrowser;
     private boolean mHasDomainUrls;
-    private DropDownPreference mAppLinkState;
+    private Preference mAppLinkState;
     private AppDomainsPreference mAppDomainUrls;
     private ClearDefaultsPreference mClearDefaultsPreference;
 
@@ -76,7 +76,19 @@
         addPreferencesFromResource(R.xml.installed_app_launch_settings);
         mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
         mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
-        mAppLinkState = (DropDownPreference) findPreference(KEY_APP_LINK_STATE);
+        mAppLinkState = findPreference(KEY_APP_LINK_STATE);
+        mAppLinkState.setOnPreferenceClickListener(preference -> {
+            final Bundle args = new Bundle();
+            args.putParcelable(KEY_PACKAGE_INFO, this.mPackageInfo);
+
+            new SubSettingLauncher(this.getContext())
+                    .setDestination(FRAGMENT_OPEN_SUPPORTED_LINKS)
+                    .setArguments(args)
+                    .setSourceMetricsCategory(SettingsEnums.APPLICATIONS_APP_LAUNCH)
+                    .setTitleRes(-1)
+                    .launch();
+            return true;
+        });
 
         mPm = getActivity().getPackageManager();
 
@@ -85,13 +97,17 @@
                 (mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
 
         if (!mIsBrowser) {
-            List<IntentFilterVerificationInfo> iviList = mPm.getIntentFilterVerifications(mPackageName);
-            List<IntentFilter> filters = mPm.getAllIntentFilters(mPackageName);
-            CharSequence[] entries = getEntries(mPackageName, iviList, filters);
+            CharSequence[] entries = getEntries(mPackageName);
             mAppDomainUrls.setTitles(entries);
             mAppDomainUrls.setValues(new int[entries.length]);
+            mAppLinkState.setEnabled(mHasDomainUrls);
+        } else {
+            // Browsers don't show the app-link prefs
+            mAppLinkState.setShouldDisableView(true);
+            mAppLinkState.setEnabled(false);
+            mAppDomainUrls.setShouldDisableView(true);
+            mAppDomainUrls.setEnabled(false);
         }
-        buildStateDropDown();
     }
 
     // An app is a "browser" if it has an activity resolution that wound up
@@ -110,95 +126,35 @@
         return false;
     }
 
-    private void buildStateDropDown() {
-        if (mIsBrowser) {
-            // Browsers don't show the app-link prefs
-            mAppLinkState.setShouldDisableView(true);
-            mAppLinkState.setEnabled(false);
-            mAppDomainUrls.setShouldDisableView(true);
-            mAppDomainUrls.setEnabled(false);
-        } else {
-            // Designed order of states in the dropdown:
-            //
-            // * always
-            // * ask
-            // * never
-            //
-            // Make sure to update linkStateToIndex() if this presentation order is changed.
-            mAppLinkState.setEntries(new CharSequence[] {
-                    getString(R.string.app_link_open_always),
-                    getString(R.string.app_link_open_ask),
-                    getString(R.string.app_link_open_never),
-            });
-            mAppLinkState.setEntryValues(new CharSequence[] {
-                    Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS),
-                    Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK),
-                    Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER),
-            });
-
-            mAppLinkState.setEnabled(mHasDomainUrls);
-            if (mHasDomainUrls) {
-                // Present 'undefined' as 'ask' because the OS treats them identically for
-                // purposes of the UI (and does the right thing around pending domain
-                // verifications that might arrive after the user chooses 'ask' in this UI).
-                final int state = mPm.getIntentVerificationStatusAsUser(mPackageName, UserHandle.myUserId());
-                mAppLinkState.setValueIndex(linkStateToIndex(state));
-
-                // Set the callback only after setting the initial selected item
-                mAppLinkState.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-                    @Override
-                    public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        return updateAppLinkState(Integer.parseInt((String) newValue));
-                    }
-                });
-            }
-        }
-    }
-
-    private int linkStateToIndex(final int state) {
+    private int linkStateToResourceId(int state) {
         switch (state) {
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
-                return 0; // Always
+                return R.string.app_link_open_always; // Always
             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
-                return 2; // Never
+                return R.string.app_link_open_never; // Never
             default:
-                return 1; // Ask
+                return R.string.app_link_open_ask; // Ask
         }
     }
 
-    private boolean updateAppLinkState(final int newState) {
-        if (mIsBrowser) {
-            // We shouldn't get into this state, but if we do make sure
-            // not to cause any permanent mayhem.
-            return false;
-        }
-
-        final int userId = UserHandle.myUserId();
-        final int priorState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
-
-        if (priorState == newState) {
-            return false;
-        }
-
-        boolean success = mPm.updateIntentVerificationStatusAsUser(mPackageName, newState, userId);
-        if (success) {
-            // Read back the state to see if the change worked
-            final int updatedState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
-            success = (newState == updatedState);
-        } else {
-            Log.e(TAG, "Couldn't update intent verification status!");
-        }
-        return success;
-    }
-
-    private CharSequence[] getEntries(String packageName, List<IntentFilterVerificationInfo> iviList,
-            List<IntentFilter> filters) {
+    private CharSequence[] getEntries(String packageName) {
         ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
         return result.toArray(new CharSequence[result.size()]);
     }
 
+    private void setAppLinkStateSummary() {
+        final int state = mPm.getIntentVerificationStatusAsUser(mPackageName,
+                UserHandle.myUserId());
+        Log.d("[sunny]", "setAppLinkStateSummary+ state=" + state);
+        mAppLinkState.setSummary(linkStateToResourceId(state));
+    }
+
     @Override
     protected boolean refreshUi() {
+        if (mHasDomainUrls) {
+            //Update the summary after return from the OpenSupportedLinks
+            setAppLinkStateSummary();
+        }
         mClearDefaultsPreference.setPackageName(mPackageName);
         mClearDefaultsPreference.setAppEntry(mAppEntry);
         return true;
diff --git a/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java b/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java
new file mode 100644
index 0000000..479d5dd
--- /dev/null
+++ b/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java
@@ -0,0 +1,184 @@
+/*
+ * 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.applications;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+/**
+ *  The radio group controller supports users to choose what kind supported links they need.
+ */
+public class AppOpenSupportedLinksPreferenceController extends BasePreferenceController
+        implements RadioButtonPreference.OnClickListener {
+    private static final String TAG = "OpenLinksPrefCtrl";
+    private static final String KEY_LINK_OPEN_ALWAYS = "app_link_open_always";
+    private static final String KEY_LINK_OPEN_ASK = "app_link_open_ask";
+    private static final String KEY_LINK_OPEN_NEVER = "app_link_open_never";
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private String mPackageName;
+    private int mCurrentIndex;
+    private PreferenceCategory mPreferenceCategory;
+    private String[] mRadioKeys = {KEY_LINK_OPEN_ALWAYS, KEY_LINK_OPEN_ASK, KEY_LINK_OPEN_NEVER};
+
+    @VisibleForTesting
+    RadioButtonPreference mAllowOpening;
+    @VisibleForTesting
+    RadioButtonPreference mAskEveryTime;
+    @VisibleForTesting
+    RadioButtonPreference mNotAllowed;
+
+    public AppOpenSupportedLinksPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    /**
+     * @param pkg selected package name.
+     * @return return controller-self.
+     */
+    public AppOpenSupportedLinksPreferenceController setInit(String pkg) {
+        mPackageName = pkg;
+        return this;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreferenceCategory = screen.findPreference(getPreferenceKey());
+        mAllowOpening = makeRadioPreference(KEY_LINK_OPEN_ALWAYS, R.string.app_link_open_always);
+        final int entriesNo = getEntriesNo();
+        //This to avoid the summary line wrap
+        mAllowOpening.setAppendixVisibility(View.GONE);
+        mAllowOpening.setSummary(
+                mContext.getResources().getQuantityString(R.plurals.app_link_open_always_summary,
+                        entriesNo, entriesNo));
+        mAskEveryTime = makeRadioPreference(KEY_LINK_OPEN_ASK, R.string.app_link_open_ask);
+        mNotAllowed = makeRadioPreference(KEY_LINK_OPEN_NEVER, R.string.app_link_open_never);
+
+        final int state = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+                UserHandle.myUserId());
+        mCurrentIndex = linkStateToIndex(state);
+        setRadioStatus(mCurrentIndex);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void onRadioButtonClicked(RadioButtonPreference preference) {
+        final int clickedIndex = preferenceKeyToIndex(preference.getKey());
+        if (mCurrentIndex != clickedIndex) {
+            mCurrentIndex = clickedIndex;
+            setRadioStatus(mCurrentIndex);
+            updateAppLinkState(indexToLinkState(mCurrentIndex));
+        }
+    }
+
+    private RadioButtonPreference makeRadioPreference(String key, int resourceId) {
+        RadioButtonPreference pref = new RadioButtonPreference(mPreferenceCategory.getContext());
+        pref.setKey(key);
+        pref.setTitle(resourceId);
+        pref.setOnClickListener(this);
+        mPreferenceCategory.addPreference(pref);
+        return pref;
+    }
+
+    private int linkStateToIndex(int state) {
+        switch (state) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+                return 0; // Always
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+                return 2; // Never
+            default:
+                return 1; // Ask
+        }
+    }
+
+    private int indexToLinkState(int index) {
+        switch (index) {
+            case 0:
+                return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+            case 2:
+                return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+            default:
+                return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+        }
+    }
+
+    private int preferenceKeyToIndex(String key) {
+        for (int i = 0; i < mRadioKeys.length; i++) {
+            if (TextUtils.equals(key, mRadioKeys[i])) {
+                return i;
+            }
+        }
+        return 1; // Ask
+    }
+
+    private void setRadioStatus(int index) {
+        mAllowOpening.setChecked(index == 0 ? true : false);
+        mAskEveryTime.setChecked(index == 1 ? true : false);
+        mNotAllowed.setChecked(index == 2 ? true : false);
+    }
+
+    private boolean updateAppLinkState(final int newState) {
+        final int userId = UserHandle.myUserId();
+        final int priorState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+                userId);
+
+        if (priorState == newState) {
+            return false;
+        }
+
+        boolean success = mPackageManager.updateIntentVerificationStatusAsUser(mPackageName,
+                newState, userId);
+        if (success) {
+            // Read back the state to see if the change worked
+            final int updatedState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+                    userId);
+            success = (newState == updatedState);
+        } else {
+            Log.e(TAG, "Couldn't update intent verification status!");
+        }
+        return success;
+    }
+
+    @VisibleForTesting
+    int getEntriesNo() {
+        return Utils.getHandledDomains(mPackageManager, mPackageName).size();
+    }
+}
diff --git a/src/com/android/settings/applications/OpenSupportedLinks.java b/src/com/android/settings/applications/OpenSupportedLinks.java
new file mode 100644
index 0000000..0e1531f
--- /dev/null
+++ b/src/com/android/settings/applications/OpenSupportedLinks.java
@@ -0,0 +1,97 @@
+/*
+ * 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.applications;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.widget.FooterPreference;
+
+/**
+ * Display the Open Supported Links page. Allow users choose what kind supported links they need.
+ */
+public class OpenSupportedLinks extends DashboardFragment {
+    private static final String TAG = "OpenSupportedLinks";
+    private static final String FOOTER_KEY = "supported_links_footer";
+
+    @VisibleForTesting
+    PackageInfo mPackageInfo;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        final Bundle args = getArguments();
+        mPackageInfo = (args != null) ? args.getParcelable(AppLaunchSettings.KEY_PACKAGE_INFO)
+                : null;
+        if (mPackageInfo == null) {
+            Log.w(TAG, "Missing PackageInfo; maybe reinstalling?");
+            return;
+        }
+        use(AppHeaderPreferenceController.class).setParentFragment(this).setPackageInfo(
+                mPackageInfo).setLifeCycle(getSettingsLifecycle());
+        use(AppOpenSupportedLinksPreferenceController.class).setInit(mPackageInfo.packageName);
+    }
+
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        super.onCreatePreferences(savedInstanceState, rootKey);
+        final FooterPreference footer = findPreference(FOOTER_KEY);
+        if (footer == null) {
+            Log.w(TAG, "Can't find the footer preference.");
+            return;
+        }
+        addLinksToFooter(footer);
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.open_supported_links;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.OPEN_SUPPORTED_LINKS;
+    }
+
+    @VisibleForTesting
+    void addLinksToFooter(FooterPreference footer) {
+        final ArraySet<String> result = Utils.getHandledDomains(getPackageManager(),
+                mPackageInfo.packageName);
+        if (result.isEmpty()) {
+            Log.w(TAG, "Can't find any app links.");
+            return;
+        }
+        CharSequence title = footer.getTitle() + System.lineSeparator();
+        for (String link : result) {
+            title = title + System.lineSeparator() + link;
+        }
+        footer.setTitle(title);
+    }
+}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index a26e359..92b8a9d 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -272,7 +272,7 @@
 
         SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(mSubscriptionId);
         if (subInfo == null) {
-            subInfo = mSubscriptionManager.getAllSubscriptionInfoList().stream().filter(
+            subInfo = mSubscriptionManager.getAvailableSubscriptionInfoList().stream().filter(
                     i -> i.getSubscriptionId() == mSubscriptionId).findFirst().orElse(null);
         }
         if (subInfo != null && mHasMobileData) {
diff --git a/src/com/android/settings/datausage/DataUsageUtils.java b/src/com/android/settings/datausage/DataUsageUtils.java
index 0308cf7..5ed62fb 100644
--- a/src/com/android/settings/datausage/DataUsageUtils.java
+++ b/src/com/android/settings/datausage/DataUsageUtils.java
@@ -166,7 +166,7 @@
         SubscriptionInfo subscriptionInfo =
                 subManager.getActiveSubscriptionInfo(subManager.getDefaultDataSubscriptionId());
         if (subscriptionInfo == null) {
-            List<SubscriptionInfo> list = subManager.getAllSubscriptionInfoList();
+            List<SubscriptionInfo> list = subManager.getAvailableSubscriptionInfoList();
             if (list.size() == 0) {
                 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
             }
diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
index db90500..7b79da7 100644
--- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java
+++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
@@ -223,9 +223,8 @@
         final Drawable icon = applicationInfo.loadIcon(context.getPackageManager());
         final Preference appPreference = new Preference(context);
         appPreference.setIcon(icon);
-        appPreference.setSummary(mSelectedApp
-                + " SDK "
-                + applicationInfo.targetSdkVersion);
+        appPreference.setSummary(getString(R.string.platform_compat_selected_app_summary,
+                                         mSelectedApp, applicationInfo.targetSdkVersion));
         appPreference.setKey(mSelectedApp);
         appPreference.setOnPreferenceClickListener(
                 preference -> {
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index 8096c0b..6abd52d 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -567,8 +567,8 @@
                 return "TD_SCDMA";
             case TelephonyManager.NETWORK_TYPE_IWLAN:
                 return "IWLAN";
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return "LTE_CA";
+//          case TelephonyManager.NETWORK_TYPE_LTE_CA:
+//              return "LTE_CA";
             case TelephonyManager.NETWORK_TYPE_NR:
                 return "NR";
             default:
diff --git a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
index 23f625f..0175b95 100644
--- a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
+++ b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
@@ -1,8 +1,9 @@
 /*
  * Copyright (C) 2019 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * 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
  *
@@ -26,6 +27,8 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.widget.LayoutPreference;
 
+import java.time.LocalTime;
+
 /**
  * Controller for activate/deactivate night mode button
  */
@@ -34,12 +37,20 @@
     private PowerManager mPowerManager;
     private Button mTurnOffButton;
     private Button mTurnOnButton;
+    private TimeFormatter mFormat;
 
     public DarkModeActivationPreferenceController(Context context,
             String preferenceKey) {
         super(context, preferenceKey);
         mPowerManager = context.getSystemService(PowerManager.class);
         mUiModeManager = context.getSystemService(UiModeManager.class);
+        mFormat = new TimeFormatter(context);
+    }
+
+    public DarkModeActivationPreferenceController(Context context,
+                                                  String preferenceKey, TimeFormatter f) {
+        this(context, preferenceKey);
+        mFormat = f;
     }
 
     @Override
@@ -58,13 +69,21 @@
     }
 
     private void updateNightMode(boolean active) {
-        final int autoMode = mUiModeManager.getNightMode();
+        final int mode = mUiModeManager.getNightMode();
         String buttonText;
 
-        if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+        if (mode == UiModeManager.MODE_NIGHT_AUTO) {
             buttonText = mContext.getString(active
                     ? R.string.dark_ui_activation_off_auto
                     : R.string.dark_ui_activation_on_auto);
+        } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+            final LocalTime time = active
+                    ? mUiModeManager.getCustomNightModeStart()
+                    : mUiModeManager.getCustomNightModeEnd();
+            final String timeStr = mFormat.of(time);
+            buttonText = mContext.getString(active
+                    ? R.string.dark_ui_activation_off_custom
+                    : R.string.dark_ui_activation_on_custom, timeStr);
         } else {
             buttonText = mContext.getString(active
                     ? R.string.dark_ui_activation_off_manual
@@ -85,11 +104,20 @@
     public CharSequence getSummary() {
         final boolean isActivated = (mContext.getResources().getConfiguration().uiMode
                 & Configuration.UI_MODE_NIGHT_YES) != 0;
-        final int autoMode = mUiModeManager.getNightMode();
-        if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+        final int mode = mUiModeManager.getNightMode();
+        if (mode == UiModeManager.MODE_NIGHT_AUTO) {
             return mContext.getString(isActivated
                     ? R.string.dark_ui_summary_on_auto_mode_auto
                     : R.string.dark_ui_summary_off_auto_mode_auto);
+        } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+            final LocalTime time = isActivated
+                    ? mUiModeManager.getCustomNightModeEnd()
+                    : mUiModeManager.getCustomNightModeStart();
+            final String timeStr = mFormat.of(time);
+
+            return mContext.getString(isActivated
+                    ? R.string.dark_ui_summary_on_auto_mode_custom
+                    : R.string.dark_ui_summary_off_auto_mode_custom, timeStr);
         } else {
             return mContext.getString(isActivated
                     ? R.string.dark_ui_summary_on_auto_mode_never
diff --git a/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java
new file mode 100644
index 0000000..5f22cb0
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java
@@ -0,0 +1,105 @@
+/*
+ * 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.display.darkmode;
+
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.app.UiModeManager;
+import android.content.Context;
+import android.text.TextUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+
+/**
+ * Controller for custom mode night mode time settings
+ */
+public class DarkModeCustomPreferenceController extends BasePreferenceController {
+    private static final String START_TIME_KEY = "dark_theme_start_time";
+    private static final String END_TIME_KEY = "dark_theme_end_time";
+    public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
+    private final UiModeManager mUiModeManager;
+    private TimeFormatter mFormat;
+    private DarkModeSettingsFragment mFragmet;
+
+    public DarkModeCustomPreferenceController(Context context, String key) {
+        super(context, key);
+        mFormat = new TimeFormatter(mContext);
+        mUiModeManager = context.getSystemService(UiModeManager.class);
+    }
+
+    public DarkModeCustomPreferenceController(
+            Context context, String key,
+            DarkModeSettingsFragment fragment) {
+        this(context, key);
+        mFragmet = fragment;
+    }
+
+    public DarkModeCustomPreferenceController(
+            Context context, String key,
+            DarkModeSettingsFragment fragment,
+            TimeFormatter format) {
+        this(context, key, fragment);
+        mFormat = format;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    public TimePickerDialog getDialog() {
+        final LocalTime initialTime;
+        if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+            initialTime = mUiModeManager.getCustomNightModeStart();
+        } else {
+            initialTime = mUiModeManager.getCustomNightModeEnd();
+        }
+        return  new TimePickerDialog(mContext, (view, hourOfDay, minute) -> {
+            final LocalTime time = LocalTime.of(hourOfDay, minute);
+            if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+                mUiModeManager.setCustomNightModeStart(time);
+            } else {
+                mUiModeManager.setCustomNightModeEnd(time);
+            }
+            if (mFragmet != null) {
+                mFragmet.refresh();
+            }
+        }, initialTime.getHour(), initialTime.getMinute(), mFormat.is24HourFormat());
+    }
+
+    @Override
+    protected void refreshSummary(Preference preference) {
+        if (mUiModeManager.getNightMode() != MODE_NIGHT_CUSTOM) {
+            preference.setVisible(false);
+            return;
+        }
+        preference.setVisible(true);
+        final LocalTime time;
+        if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+            time = mUiModeManager.getCustomNightModeStart();
+        } else {
+            time = mUiModeManager.getCustomNightModeEnd();
+        }
+        preference.setSummary(mFormat.of(time));
+    }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModePreference.java b/src/com/android/settings/display/darkmode/DarkModePreference.java
index c5fbded..7fb8023 100644
--- a/src/com/android/settings/display/darkmode/DarkModePreference.java
+++ b/src/com/android/settings/display/darkmode/DarkModePreference.java
@@ -22,6 +22,8 @@
 import com.android.settings.R;
 import com.android.settings.widget.MasterSwitchPreference;
 
+import java.time.LocalTime;
+
 /**
  * component for the display settings dark ui summary*/
 public class DarkModePreference extends MasterSwitchPreference {
@@ -31,11 +33,14 @@
     private PowerManager mPowerManager;
     private Runnable mCallback;
 
+    private TimeFormatter mFormat;
+
     public DarkModePreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         mDarkModeObserver = new DarkModeObserver(context);
         mUiModeManager = context.getSystemService(UiModeManager.class);
         mPowerManager = context.getSystemService(PowerManager.class);
+        mFormat = new TimeFormatter(context);
         mCallback = () -> {
             final boolean batterySaver = mPowerManager.isPowerSaveMode();
             final boolean active = (getContext().getResources().getConfiguration().uiMode
@@ -60,21 +65,30 @@
 
     private void updateSummary(boolean batterySaver, boolean active) {
         if (batterySaver) {
-            final int stringId = active ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
+            final int stringId = active
+                    ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
                     : R.string.dark_ui_mode_disabled_summary_dark_theme_off;
             setSummary(getContext().getString(stringId));
             return;
         }
-        final boolean auto = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO;
-
+        final int mode = mUiModeManager.getNightMode();
         String detail;
-        if (active) {
-            detail = getContext().getString(auto
+
+        if (mode == UiModeManager.MODE_NIGHT_AUTO) {
+            detail = getContext().getString(active
                     ? R.string.dark_ui_summary_on_auto_mode_auto
-                    : R.string.dark_ui_summary_on_auto_mode_never);
+                    : R.string.dark_ui_summary_off_auto_mode_auto);
+        } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+            final LocalTime time = active
+                    ? mUiModeManager.getCustomNightModeEnd()
+                    : mUiModeManager.getCustomNightModeStart();
+            final String timeStr = mFormat.of(time);
+            detail = getContext().getString(active
+                    ? R.string.dark_ui_summary_on_auto_mode_custom
+                    : R.string.dark_ui_summary_off_auto_mode_custom, timeStr);
         } else {
-            detail = getContext().getString(auto
-                    ? R.string.dark_ui_summary_off_auto_mode_auto
+            detail = getContext().getString(active
+                    ? R.string.dark_ui_summary_on_auto_mode_never
                     : R.string.dark_ui_summary_off_auto_mode_never);
         }
         String summary = getContext().getString(active
diff --git a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
index 9a86757..ea7fc03 100644
--- a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
+++ b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
@@ -63,8 +63,17 @@
     }
 
     private int getCurrentMode() {
-        final int resId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO
-                ? R.string.dark_ui_auto_mode_auto : R.string.dark_ui_auto_mode_never;
+        int resId;
+        switch (mUiModeManager.getNightMode()) {
+            case UiModeManager.MODE_NIGHT_AUTO:
+                resId = R.string.dark_ui_auto_mode_auto;
+                break;
+            case UiModeManager.MODE_NIGHT_CUSTOM:
+                resId = R.string.dark_ui_auto_mode_custom;
+                break;
+            default:
+                resId = R.string.dark_ui_auto_mode_never;
+        }
         return mPreference.findIndexOfValue(mContext.getString(resId));
     }
 
@@ -85,6 +94,9 @@
         } else if (mCurrentMode == mPreference.findIndexOfValue(
                 mContext.getString(R.string.dark_ui_auto_mode_auto))) {
             mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO);
+        } else if (mCurrentMode == mPreference.findIndexOfValue(
+                mContext.getString(R.string.dark_ui_auto_mode_custom))) {
+            mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM);
         }
         return true;
     }
diff --git a/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
index 6293316..97ee221 100644
--- a/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
+++ b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
@@ -14,14 +14,23 @@
 
 package com.android.settings.display.darkmode;
 
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.app.UiModeManager;
 import android.content.Context;
 import android.os.Bundle;
 import android.app.settings.SettingsEnums;
+import androidx.preference.Preference;
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
 
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Settings screen for Dark UI Mode
  */
@@ -29,15 +38,20 @@
 public class DarkModeSettingsFragment extends DashboardFragment {
 
     private static final String TAG = "DarkModeSettingsFragment";
+    private static final String DARK_THEME_END_TIME = "dark_theme_end_time";
+    private static final String DARK_THEME_START_TIME = "dark_theme_start_time";
     private DarkModeObserver mContentObserver;
+    private DarkModeCustomPreferenceController mCustomStartController;
+    private DarkModeCustomPreferenceController mCustomEndController;
     private Runnable mCallback = () -> {
         updatePreferenceStates();
     };
+    private static final int DIALOG_START_TIME = 0;
+    private static final int DIALOG_END_TIME = 1;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         final Context context = getContext();
         mContentObserver = new DarkModeObserver(context);
     }
@@ -50,6 +64,18 @@
     }
 
     @Override
+    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+        List<AbstractPreferenceController> controllers =  new ArrayList(2);
+        mCustomStartController = new DarkModeCustomPreferenceController(getContext(),
+                DARK_THEME_START_TIME, this);
+        mCustomEndController = new DarkModeCustomPreferenceController(getContext(),
+                DARK_THEME_END_TIME, this);
+        controllers.add(mCustomStartController);
+        controllers.add(mCustomEndController);
+        return controllers;
+    }
+
+    @Override
     public void onStop() {
         super.onStop();
         // Stop listening for state changes.
@@ -57,6 +83,34 @@
     }
 
     @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (DARK_THEME_END_TIME.equals(preference.getKey())) {
+            showDialog(DIALOG_END_TIME);
+            return true;
+        } else if (DARK_THEME_START_TIME.equals(preference.getKey())) {
+            showDialog(DIALOG_START_TIME);
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    public void refresh() {
+        this.updatePreferenceStates();
+    }
+
+    @Override
+    public Dialog onCreateDialog(final int dialogId) {
+        if (dialogId == DIALOG_START_TIME || dialogId == DIALOG_END_TIME) {
+            if (dialogId == DIALOG_START_TIME) {
+                return mCustomStartController.getDialog();
+            } else {
+                return mCustomEndController.getDialog();
+            }
+        }
+        return super.onCreateDialog(dialogId);
+    }
+
+    @Override
     protected int getPreferenceScreenResId() {
         return R.xml.dark_mode_settings;
     }
@@ -76,6 +130,18 @@
         return SettingsEnums.DARK_UI_SETTINGS;
     }
 
+    @Override
+    public int getDialogMetricsCategory(int dialogId) {
+        switch (dialogId) {
+            case DIALOG_START_TIME:
+                return SettingsEnums.DIALOG_DARK_THEME_SET_START_TIME;
+            case DIALOG_END_TIME:
+                return SettingsEnums.DIALOG_DARK_THEME_SET_END_TIME;
+            default:
+                return 0;
+        }
+    }
+
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider(R.xml.dark_mode_settings);
 }
diff --git a/src/com/android/settings/display/darkmode/TimeFormatter.java b/src/com/android/settings/display/darkmode/TimeFormatter.java
new file mode 100644
index 0000000..87d22da
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/TimeFormatter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.display.darkmode;
+
+import android.content.Context;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Formats LocalTime to the locale time string format
+*/
+public class TimeFormatter {
+    private final Context mContext;
+    public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
+    public TimeFormatter(Context context) {
+        mContext = context;
+    }
+
+    public String of(LocalTime time) {
+        return is24HourFormat() ? time.toString() : formatter.format(time);
+    }
+
+    public boolean is24HourFormat() {
+        return android.text.format.DateFormat.is24HourFormat(mContext);
+    }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
index e11ce30..4a02d91 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
@@ -80,10 +80,6 @@
                         LegacySuggestionContextualCardController.class,
                         LegacySuggestionContextualCardRenderer.class));
                 add(new ControllerRendererMapping(CardType.SLICE,
-                        SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP,
-                        SliceContextualCardController.class,
-                        SliceContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.SLICE,
                         SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
                         SliceContextualCardController.class,
                         SliceContextualCardRenderer.class));
@@ -91,6 +87,10 @@
                         SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
                         SliceContextualCardController.class,
                         SliceContextualCardRenderer.class));
+                add(new ControllerRendererMapping(CardType.SLICE,
+                        SliceContextualCardRenderer.VIEW_TYPE_STICKY,
+                        SliceContextualCardController.class,
+                        SliceContextualCardRenderer.class));
                 add(new ControllerRendererMapping(CardType.CONDITIONAL_FOOTER,
                         ConditionFooterContextualCardRenderer.VIEW_TYPE,
                         ConditionContextualCardController.class,
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index d02f103..9e9069f 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -17,13 +17,15 @@
 package com.android.settings.homepage.contextualcards;
 
 import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
-import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE;
 import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE;
+import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI;
+import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
 
 import static java.util.stream.Collectors.groupingBy;
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
+import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.format.DateUtils;
@@ -52,6 +54,7 @@
 import com.android.settingslib.core.lifecycle.events.OnStop;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -81,6 +84,8 @@
     static final String KEY_CONTEXTUAL_CARDS = "key_contextual_cards";
 
     private static final String TAG = "ContextualCardManager";
+    private static final List<Uri> STICKY_CARDS =
+            Arrays.asList(CONTEXTUAL_WIFI_SLICE_URI, BLUETOOTH_DEVICES_SLICE_URI);
 
     private final Context mContext;
     private final Lifecycle mLifecycle;
@@ -310,7 +315,7 @@
             return cards;
         }
 
-        final List<ContextualCard> result = getCardsWithDeferredSetupViewType(cards);
+        final List<ContextualCard> result = getCardsWithStickyViewType(cards);
         return getCardsWithSuggestionViewType(result);
     }
 
@@ -341,17 +346,24 @@
         return result;
     }
 
-    private List<ContextualCard> getCardsWithDeferredSetupViewType(List<ContextualCard> cards) {
-        // Find the deferred setup card and assign it with proper view type.
-        // Reason: The returned card list will mix deferred setup card and other suggestion cards
-        // after device running 1 days.
+    // TODO(b/143055685):use category to determine whether they are sticky.
+    private List<ContextualCard> getCardsWithStickyViewType(List<ContextualCard> cards) {
         final List<ContextualCard> result = new ArrayList<>(cards);
+        int replaceCount = 0;
         for (int index = 0; index < result.size(); index++) {
+            if (replaceCount > STICKY_CARDS.size() - 1) {
+                break;
+            }
+
             final ContextualCard card = cards.get(index);
-            if (card.getCategory() == DEFERRED_SETUP_VALUE) {
+            if (card.getCardType() != ContextualCard.CardType.SLICE) {
+                continue;
+            }
+
+            if (STICKY_CARDS.contains(card.getSliceUri())) {
                 result.set(index, card.mutate().setViewType(
-                        SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP).build());
-                return result;
+                        SliceContextualCardRenderer.VIEW_TYPE_STICKY).build());
+                replaceCount++;
             }
         }
         return result;
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
index cf1e61f..3d75962 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
 import android.app.PendingIntent;
 import android.app.settings.SettingsEnums;
 import android.bluetooth.BluetoothAdapter;
@@ -38,6 +40,7 @@
 import com.android.settings.SubSettings;
 import com.android.settings.Utils;
 import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
+import com.android.settings.bluetooth.BluetoothPairingDetail;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.slices.CustomSliceRegistry;
@@ -64,17 +67,18 @@
      * than {@link #DEFAULT_EXPANDED_ROW_COUNT}.
      */
     @VisibleForTesting
-    static final int DEFAULT_EXPANDED_ROW_COUNT = 3;
+    static final int DEFAULT_EXPANDED_ROW_COUNT = 2;
 
     /**
      * Refer {@link com.android.settings.bluetooth.BluetoothDevicePreference#compareTo} to sort the
      * Bluetooth devices by {@link CachedBluetoothDevice}.
      */
-    private static final Comparator<CachedBluetoothDevice> COMPARATOR
-            = Comparator.naturalOrder();
+    private static final Comparator<CachedBluetoothDevice> COMPARATOR = Comparator.naturalOrder();
 
     private static final String TAG = "BluetoothDevicesSlice";
 
+    private static int sToggledState;
+
     private final Context mContext;
 
     public BluetoothDevicesSlice(Context context) {
@@ -88,43 +92,52 @@
 
     @Override
     public Slice getSlice() {
+        final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (btAdapter == null) {
+            Log.i(TAG, "Bluetooth is not supported on this hardware platform");
+            return null;
+        }
+
         // Reload theme for switching dark mode on/off
         mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);
 
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 com.android.internal.R.drawable.ic_settings_bluetooth);
-        final CharSequence title = mContext.getText(R.string.bluetooth_devices);
-        final CharSequence titleNoBluetoothDevices = mContext.getText(
-                R.string.no_bluetooth_devices);
+        final CharSequence title = mContext.getText(R.string.bluetooth_settings_title);
         final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, 0,
                 getIntent(), 0);
         final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryActionIntent, icon,
                 ListBuilder.ICON_IMAGE, title);
-        final ListBuilder listBuilder =
-                new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
-                        .setAccentColor(COLOR_NOT_TINTED);
+        final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+                .setAccentColor(COLOR_NOT_TINTED)
+                .setHeader(new ListBuilder.HeaderBuilder()
+                        .setTitle(title)
+                        .setPrimaryAction(primarySliceAction));
+
+        // Only show a toggle when Bluetooth is off and not turning on.
+        if ((!isBluetoothEnabled(btAdapter) && sToggledState != BluetoothAdapter.STATE_TURNING_ON)
+                || sToggledState == BluetoothAdapter.STATE_TURNING_OFF) {
+            sToggledState = 0;
+            final PendingIntent toggleAction = getBroadcastIntent(mContext);
+            final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+                    null /* actionTitle */, false /* isChecked */);
+            return listBuilder
+                    .addAction(toggleSliceAction)
+                    .build();
+        }
+        sToggledState = 0;
 
         // Get row builders by Bluetooth devices.
         final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilder();
-
-        // Return a header with IsError flag, if no Bluetooth devices.
         if (rows.isEmpty()) {
-            return listBuilder.setHeader(new ListBuilder.HeaderBuilder()
-                    .setTitle(titleNoBluetoothDevices)
-                    .setPrimaryAction(primarySliceAction))
-                    .setIsError(true)
+            return listBuilder
+                    .addRow(getPairNewDeviceRow())
                     .build();
         }
 
         // Get displayable device count.
         final int deviceCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);
 
-        // According to the displayable device count to set sub title of header.
-        listBuilder.setHeader(new ListBuilder.HeaderBuilder()
-                .setTitle(title)
-                .setSubtitle(getSubTitle(deviceCount))
-                .setPrimaryAction(primarySliceAction));
-
         // According to the displayable device count to add bluetooth device rows.
         for (int i = 0; i < deviceCount; i++) {
             listBuilder.addRow(rows.get(i));
@@ -148,6 +161,20 @@
 
     @Override
     public void onNotifyChange(Intent intent) {
+        final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+        final boolean currentState = isBluetoothEnabled(btAdapter);
+        final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, currentState);
+        if (newState != currentState) {
+            if (newState) {
+                sToggledState = BluetoothAdapter.STATE_TURNING_ON;
+                btAdapter.enable();
+            } else {
+                sToggledState = BluetoothAdapter.STATE_TURNING_OFF;
+                btAdapter.disable();
+            }
+            mContext.getContentResolver().notifyChange(getUri(), null);
+        }
+
         // Activate available media device.
         final int bluetoothDeviceHashCode = intent.getIntExtra(BLUETOOTH_DEVICE_HASH_CODE, -1);
         for (CachedBluetoothDevice cachedBluetoothDevice : getConnectedBluetoothDevices()) {
@@ -203,7 +230,7 @@
 
         // The requestCode should be unique, use the hashcode of device as request code.
         return PendingIntent
-                .getActivity(mContext, device.hashCode()  /* requestCode */,
+                .getActivity(mContext, device.hashCode() /* requestCode */,
                         subSettingLauncher.toIntent(),
                         0  /* flags */);
     }
@@ -223,6 +250,23 @@
         return Utils.createIconWithDrawable(drawable);
     }
 
+    private ListBuilder.RowBuilder getPairNewDeviceRow() {
+        final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_add_24dp);
+        final String title = mContext.getString(R.string.bluetooth_pairing_pref_title);
+        final Intent intent = new SubSettingLauncher(mContext)
+                .setDestination(BluetoothPairingDetail.class.getName())
+                .setTitleRes(R.string.bluetooth_pairing_page_title)
+                .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_PAIRING)
+                .toIntent();
+        final PendingIntent pi = PendingIntent.getActivity(mContext, intent.hashCode(), intent,
+                0 /* flags */);
+        final SliceAction action = SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE,
+                title);
+        return new ListBuilder.RowBuilder()
+                .setTitleItem(action)
+                .setTitle(title);
+    }
+
     private List<ListBuilder.RowBuilder> getBluetoothRowBuilder() {
         // According to Bluetooth devices to create row builders.
         final List<ListBuilder.RowBuilder> bluetoothRows = new ArrayList<>();
@@ -272,8 +316,13 @@
                 bluetoothDevice.getName());
     }
 
-    private CharSequence getSubTitle(int deviceCount) {
-        return mContext.getResources().getQuantityString(R.plurals.show_bluetooth_devices,
-                deviceCount, deviceCount);
+    private boolean isBluetoothEnabled(BluetoothAdapter btAdapter) {
+        switch (btAdapter.getState()) {
+            case BluetoothAdapter.STATE_ON:
+            case BluetoothAdapter.STATE_TURNING_ON:
+                return true;
+            default:
+                return false;
+        }
     }
 }
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
index 45be727..6f07d47 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
@@ -54,7 +54,7 @@
 public class SliceContextualCardRenderer implements ContextualCardRenderer, LifecycleObserver {
     public static final int VIEW_TYPE_FULL_WIDTH = R.layout.contextual_slice_full_tile;
     public static final int VIEW_TYPE_HALF_WIDTH = R.layout.contextual_slice_half_tile;
-    public static final int VIEW_TYPE_DEFERRED_SETUP = R.layout.contextual_slice_deferred_setup;
+    public static final int VIEW_TYPE_STICKY = R.layout.contextual_slice_sticky_tile;
 
     private static final String TAG = "SliceCardRenderer";
 
@@ -66,7 +66,6 @@
     private final Context mContext;
     private final LifecycleOwner mLifecycleOwner;
     private final ControllerRendererPool mControllerRendererPool;
-    private final SliceDeferredSetupCardRendererHelper mDeferredSetupCardHelper;
     private final SliceFullCardRendererHelper mFullCardHelper;
     private final SliceHalfCardRendererHelper mHalfCardHelper;
 
@@ -80,14 +79,11 @@
         mLifecycleOwner.getLifecycle().addObserver(this);
         mFullCardHelper = new SliceFullCardRendererHelper(context);
         mHalfCardHelper = new SliceHalfCardRendererHelper(context);
-        mDeferredSetupCardHelper = new SliceDeferredSetupCardRendererHelper(context);
     }
 
     @Override
     public RecyclerView.ViewHolder createViewHolder(View view, @LayoutRes int viewType) {
-        if (viewType == VIEW_TYPE_DEFERRED_SETUP) {
-            return mDeferredSetupCardHelper.createViewHolder(view);
-        } else if (viewType == VIEW_TYPE_HALF_WIDTH) {
+        if (viewType == VIEW_TYPE_HALF_WIDTH) {
             return mHalfCardHelper.createViewHolder(view);
         }
         return mFullCardHelper.createViewHolder(view);
@@ -132,9 +128,7 @@
                 return;
             }
 
-            if (holder.getItemViewType() == VIEW_TYPE_DEFERRED_SETUP) {
-                mDeferredSetupCardHelper.bindView(holder, card, slice);
-            } else if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
+            if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
                 mHalfCardHelper.bindView(holder, card, slice);
             } else {
                 mFullCardHelper.bindView(holder, card, slice);
@@ -144,11 +138,7 @@
             }
         });
 
-        if (holder.getItemViewType()
-                == VIEW_TYPE_DEFERRED_SETUP) {// Deferred setup is never dismissible.
-        } else if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
-            initDismissalActions(holder, card);
-        } else {
+        if (holder.getItemViewType() != VIEW_TYPE_STICKY) {
             initDismissalActions(holder, card);
         }
 
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
deleted file mode 100644
index ea9e424..0000000
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.slices;
-
-import android.app.PendingIntent;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.Slice;
-import androidx.slice.SliceMetadata;
-import androidx.slice.core.SliceAction;
-import androidx.slice.widget.EventInfo;
-
-import com.android.settings.R;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-/**
- * Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
- */
-class SliceDeferredSetupCardRendererHelper {
-    private static final String TAG = "SliceDSCRendererHelper";
-
-    private final Context mContext;
-
-    SliceDeferredSetupCardRendererHelper(Context context) {
-        mContext = context;
-    }
-
-    RecyclerView.ViewHolder createViewHolder(View view) {
-        return new DeferredSetupCardViewHolder(view);
-    }
-
-    void bindView(RecyclerView.ViewHolder holder, ContextualCard card, Slice slice) {
-        final DeferredSetupCardViewHolder view = (DeferredSetupCardViewHolder) holder;
-        final SliceMetadata sliceMetadata = SliceMetadata.from(mContext, slice);
-        final SliceAction primaryAction = sliceMetadata.getPrimaryAction();
-        view.icon.setImageDrawable(primaryAction.getIcon().loadDrawable(mContext));
-        view.title.setText(primaryAction.getTitle());
-        view.summary.setText(sliceMetadata.getSubtitle());
-        view.button.setOnClickListener(v -> {
-            try {
-                primaryAction.getAction().send();
-            } catch (PendingIntent.CanceledException e) {
-                Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
-            }
-            final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
-                    EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
-
-            final MetricsFeatureProvider metricsFeatureProvider =
-                    FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
-
-            metricsFeatureProvider.action(mContext,
-                    SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
-        });
-    }
-
-    static class DeferredSetupCardViewHolder extends RecyclerView.ViewHolder {
-        public final LinearLayout content;
-        public final ImageView icon;
-        public final TextView title;
-        public final TextView summary;
-        public final Button button;
-
-        public DeferredSetupCardViewHolder(View itemView) {
-            super(itemView);
-            content = itemView.findViewById(R.id.content);
-            icon = itemView.findViewById(android.R.id.icon);
-            title = itemView.findViewById(android.R.id.title);
-            summary = itemView.findViewById(android.R.id.summary);
-            button = itemView.findViewById(R.id.finish_setup);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/network/OWNERS b/src/com/android/settings/network/OWNERS
index bedbe16..d23f816 100644
--- a/src/com/android/settings/network/OWNERS
+++ b/src/com/android/settings/network/OWNERS
@@ -1,6 +1,8 @@
 # Default reviewers for this and subdirectories.
+allenwtsu@google.com
 andychou@google.com
 bonianchen@google.com
-allenwtsu@google.com
+songferngwang@google.com
+tomhsu@google.com
 
 # Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 41c7c14..9105db8 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -32,7 +32,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class SubscriptionUtil {
     private static final String TAG = "SubscriptionUtil";
@@ -79,12 +81,7 @@
         if (sAvailableResultsForTesting != null) {
             return sAvailableResultsForTesting;
         }
-        final SubscriptionManager subMgr = context.getSystemService(SubscriptionManager.class);
-
-        final List<SubscriptionInfo> subscriptions =
-                new ArrayList<>(emptyIfNull(subMgr.getSelectableSubscriptionInfoList()));
-
-        return subscriptions;
+        return new ArrayList<>(emptyIfNull(getSelectableSubscriptionInfoList(context)));
     }
 
     /**
@@ -242,4 +239,70 @@
         }
         return info.getSimSlotIndex();
     }
+
+    /**
+     * Return a list of subscriptions that are available and visible to the user.
+     *
+     * @return list of user selectable subscriptions.
+     */
+    public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
+        SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+        List<SubscriptionInfo> availableList = subManager.getAvailableSubscriptionInfoList();
+        if (availableList == null) {
+            return null;
+        } else {
+            // Multiple subscriptions in a group should only have one representative.
+            // It should be the current active primary subscription if any, or any
+            // primary subscription.
+            List<SubscriptionInfo> selectableList = new ArrayList<>();
+            Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>();
+
+            for (SubscriptionInfo info : availableList) {
+                // Opportunistic subscriptions are considered invisible
+                // to users so they should never be returned.
+                if (!isSubscriptionVisible(subManager, context, info)) continue;
+
+                ParcelUuid groupUuid = info.getGroupUuid();
+                if (groupUuid == null) {
+                    // Doesn't belong to any group. Add in the list.
+                    selectableList.add(info);
+                } else if (!groupMap.containsKey(groupUuid)
+                        || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX
+                        && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) {
+                    // If it belongs to a group that has never been recorded or it's the current
+                    // active subscription, add it in the list.
+                    selectableList.remove(groupMap.get(groupUuid));
+                    selectableList.add(info);
+                    groupMap.put(groupUuid, info);
+                }
+
+            }
+            return selectableList;
+        }
+    }
+
+
+    /**
+     * Whether a subscription is visible to API caller. If it's a bundled opportunistic
+     * subscription, it should be hidden anywhere in Settings, dialer, status bar etc.
+     * Exception is if caller owns carrier privilege, in which case they will
+     * want to see their own hidden subscriptions.
+     *
+     * @param info the subscriptionInfo to check against.
+     * @return true if this subscription should be visible to the API caller.
+     */
+    private static boolean isSubscriptionVisible(
+            SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) {
+        if (info == null) return false;
+        // If subscription is NOT grouped opportunistic subscription, it's visible.
+        if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
+
+        // If the caller is the carrier app and owns the subscription, it should be visible
+        // to the caller.
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(info.getSubscriptionId());
+        boolean hasCarrierPrivilegePermission = telephonyManager.hasCarrierPrivileges()
+                || subscriptionManager.canManageSubscription(info);
+        return hasCarrierPrivilegePermission;
+    }
 }
diff --git a/src/com/android/settings/network/ims/ImsDirectQuery.java b/src/com/android/settings/network/ims/ImsDirectQuery.java
new file mode 100644
index 0000000..4c6a932
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsDirectQuery.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.network.ims;
+
+
+/**
+ * An interface for direct querying IMS, and return {@code boolean}
+ */
+public interface ImsDirectQuery {
+
+    /**
+     * Interface for performing IMS status/configuration query through public APIs
+     *
+     * @return result of query in boolean
+     */
+    boolean directQuery();
+
+}
diff --git a/src/com/android/settings/network/ims/ImsDirectQueryImpl.java b/src/com/android/settings/network/ims/ImsDirectQueryImpl.java
new file mode 100644
index 0000000..cff8461
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsDirectQueryImpl.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ims;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+
+
+/**
+ * An implementation of {@code ImsQuery} and {@code ImsDirectQuery}.
+ */
+abstract class ImsDirectQueryImpl implements ImsQuery, ImsDirectQuery, Callable<Boolean> {
+
+    /**
+     * Implementation of interface {@code ImsQuery}
+     *
+     * @param executors {@code ExecutorService} which allows to submit {@code ImsQuery} when
+     * required
+     * @return result of query in format of {@code Future<Boolean>}
+     */
+    public Future<Boolean> query(ExecutorService executors) throws RejectedExecutionException {
+        return executors.submit(this);
+    }
+
+    /**
+     * Implementation of interface {@code ImsDirectQuery}
+     *
+     * @return result of query
+     */
+    public boolean directQuery() {
+        return call();
+    }
+
+    /**
+     * Query running within a {@code Callable}
+     *
+     * @return result of query
+     */
+    public abstract Boolean call();
+}
diff --git a/src/com/android/settings/network/ims/ImsQuery.java b/src/com/android/settings/network/ims/ImsQuery.java
new file mode 100644
index 0000000..1462718
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQuery.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ims;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+
+
+/**
+ * An interface for querying IMS, and return {@code Future<Boolean>}
+ */
+public interface ImsQuery {
+
+    /**
+     * Interface for performing IMS status/configuration query through ExecutorService
+     *
+     * @param executors {@code ExecutorService} which allows to submit {@code ImsQuery} when
+     * required
+     * @return result of query in format of {@code Future<Boolean>}
+     */
+    Future<Boolean> query(ExecutorService executors) throws RejectedExecutionException;
+
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryController.java b/src/com/android/settings/network/ims/ImsQueryController.java
new file mode 100644
index 0000000..651c422
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryController.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.network.ims;
+
+import android.content.Context;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Controller class for querying IMS status
+ */
+abstract class ImsQueryController {
+
+    @VisibleForTesting
+    ImsDirectQuery isSystemTtyEnabled(Context context) {
+        return new ImsQuerySystemTtyStat(context);
+    }
+
+    @VisibleForTesting
+    ImsDirectQuery isTtyOnVolteEnabled(int subId) {
+        return new ImsQueryTtyOnVolteStat(subId);
+    }
+}
diff --git a/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java b/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java
new file mode 100644
index 0000000..4239b16
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ims;
+
+import android.content.Context;
+import android.telecom.TelecomManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing system TTY stat
+ */
+public class ImsQuerySystemTtyStat extends ImsDirectQueryImpl {
+
+    /**
+     * Constructor
+     * @param context context of activity
+     */
+    public ImsQuerySystemTtyStat(Context context) {
+        mContext = context;
+    }
+
+    private volatile Context mContext;
+
+    /**
+     * Query running within a {@code Callable}
+     *
+     * @return result of query
+     */
+    public Boolean call() {
+        final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+        return (telecomManager.getCurrentTtyMode() != TelecomManager.TTY_MODE_OFF);
+    }
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
new file mode 100644
index 0000000..a371ce7
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ims;
+
+import android.telephony.ims.ImsMmTelManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing IMS tty on VoLte stat
+ */
+public class ImsQueryTtyOnVolteStat extends ImsDirectQueryImpl {
+
+    /**
+     * Constructor
+     * @param subId subscription id
+     */
+    public ImsQueryTtyOnVolteStat(int subId) {
+        mSubId = subId;
+    }
+
+    private volatile int mSubId;
+
+    /**
+     * Query running within a {@code Callable}
+     *
+     * @return result of query
+     */
+    public Boolean call() {
+        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+        return imsMmTelManager.isTtyOverVolteEnabled();
+    }
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
new file mode 100644
index 0000000..86e25e6
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ims;
+
+import android.telephony.ims.ImsMmTelManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing IMS WFC enabled settings from user
+ */
+public class ImsQueryWfcUserSetting extends ImsDirectQueryImpl {
+
+    /**
+     * Constructor
+     * @param subId subscription id
+     */
+    public ImsQueryWfcUserSetting(int subId) {
+        mSubId = subId;
+    }
+
+    private volatile int mSubId;
+
+    /**
+     * Query running within a {@code Callable}
+     *
+     * @return result of query
+     */
+    public Boolean call() {
+        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+        return imsMmTelManager.isVoWiFiSettingEnabled();
+    }
+}
diff --git a/src/com/android/settings/network/ims/VolteQueryImsState.java b/src/com/android/settings/network/ims/VolteQueryImsState.java
new file mode 100644
index 0000000..b999cda
--- /dev/null
+++ b/src/com/android/settings/network/ims/VolteQueryImsState.java
@@ -0,0 +1,72 @@
+/*
+ * 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.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.ims.ImsManager;
+import com.android.settings.network.SubscriptionUtil;
+
+
+/**
+ * Controller class for querying Volte status
+ */
+public class VolteQueryImsState extends ImsQueryController {
+
+    private Context mContext;
+    private int mSubId;
+
+    /**
+     * Constructor
+     *
+     * @param context {@code Context}
+     * @param subId subscription's id
+     */
+    public VolteQueryImsState(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Get allowance status for user to alter configuration
+     *
+     * @return true when changing configuration by user is allowed.
+     */
+    public boolean isAllowUserControl() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+
+        return ((!isSystemTtyEnabled(mContext).directQuery())
+                || (isTtyOnVolteEnabled(mSubId).directQuery()));
+    }
+
+    /**
+     * Get user's configuration
+     *
+     * @return true when user's configuration is ON otherwise false.
+     */
+    public boolean isEnabledByUser() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+        ImsManager imsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
+                    mContext, mSubId));
+        return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
+    }
+}
diff --git a/src/com/android/settings/network/ims/VtQueryImsState.java b/src/com/android/settings/network/ims/VtQueryImsState.java
new file mode 100644
index 0000000..38306cd
--- /dev/null
+++ b/src/com/android/settings/network/ims/VtQueryImsState.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.ims.ImsManager;
+import com.android.settings.network.SubscriptionUtil;
+
+/**
+ * Controller class for querying VT status
+ */
+public class VtQueryImsState {
+
+    private Context mContext;
+    private int mSubId;
+
+    /**
+     * Constructor
+     *
+     * @param context {@code Context}
+     * @param subId subscription's id
+     */
+    public VtQueryImsState(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Get user's configuration
+     *
+     * @return true when user's configuration is ON otherwise false.
+     */
+    public boolean isEnabledByUser() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+        ImsManager imsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
+                    mContext, mSubId));
+        return imsManager.isVtEnabledByUser();
+    }
+}
diff --git a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java
new file mode 100644
index 0000000..f8b0767
--- /dev/null
+++ b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java
@@ -0,0 +1,76 @@
+/*
+ * 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.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Controller class for querying Wifi calling status
+ */
+public class WifiCallingQueryImsState extends ImsQueryController  {
+
+    private Context mContext;
+    private int mSubId;
+
+    /**
+     * Constructor
+     *
+     * @param context {@code Context}
+     * @param subId subscription's id
+     */
+    public WifiCallingQueryImsState(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Implementation of ImsQueryController#isEnabledByUser(int subId)
+     */
+    @VisibleForTesting
+    ImsDirectQuery isEnabledByUser(int subId) {
+        return new ImsQueryWfcUserSetting(subId);
+    }
+
+    /**
+     * Get allowance status for user to alter configuration
+     *
+     * @return true when changing configuration by user is allowed.
+     */
+    public boolean isAllowUserControl() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+
+        return ((!isSystemTtyEnabled(mContext).directQuery())
+                || (isTtyOnVolteEnabled(mSubId).directQuery()));
+    }
+
+    /**
+     * Get user's configuration
+     *
+     * @return true when user's configuration is ON otherwise false.
+     */
+    public boolean isEnabledByUser() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+        return isEnabledByUser(mSubId).directQuery();
+    }
+}
diff --git a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
index eabebd1..123e100 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
@@ -22,9 +22,11 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -33,6 +35,7 @@
 
 import com.android.ims.ImsManager;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -46,7 +49,10 @@
 public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController
         implements LifecycleObserver, OnStart, OnStop {
 
-    private Preference mPreference;
+    private static final String TAG = "Enhanced4g";
+
+    @VisibleForTesting
+    Preference mPreference;
     private CarrierConfigManager mCarrierConfigManager;
     private PersistableBundle mCarrierConfig;
     @VisibleForTesting
@@ -70,12 +76,12 @@
     }
 
     public Enhanced4gBasePreferenceController init(int subId) {
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && mSubId == subId) {
+        if (SubscriptionManager.isValidSubscriptionId(mSubId) && mSubId == subId) {
             return this;
         }
         mSubId = subId;
         mCarrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
             mImsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
                     mContext, mSubId));
         }
@@ -102,14 +108,16 @@
             return CONDITIONALLY_UNAVAILABLE;
         }
         final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
-        final boolean isVisible = subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+        final VolteQueryImsState queryState = queryImsState(subId);
+        final boolean isVisible = SubscriptionManager.isValidSubscriptionId(subId)
                 && mImsManager != null && carrierConfig != null
                 && mImsManager.isVolteEnabledByPlatform()
                 && isVolteProvisionedOnDevice(mSubId)
                 && MobileNetworkUtils.isImsServiceStateReady(mImsManager)
                 && !carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL);
         return isVisible
-                ? (isPrefEnabled() ? AVAILABLE : AVAILABLE_UNSEARCHABLE)
+                ? (isUserControlAllowed() && queryState.isAllowUserControl()
+                ? AVAILABLE : AVAILABLE_UNSEARCHABLE)
                 : CONDITIONALLY_UNAVAILABLE;
     }
 
@@ -134,14 +142,28 @@
         super.updateState(preference);
         final SwitchPreference switchPreference = (SwitchPreference) preference;
 
-        switchPreference.setEnabled(isPrefEnabled());
-        switchPreference.setChecked(mImsManager.isEnhanced4gLteModeSettingEnabledByUser()
-                && mImsManager.isNonTtyOrTtyOnVolteEnabled());
+        final VolteQueryImsState queryState = queryImsState(mSubId);
+        switchPreference.setEnabled(isUserControlAllowed()
+                && queryState.isAllowUserControl());
+        switchPreference.setChecked(queryState.isEnabledByUser()
+                && queryState.isAllowUserControl());
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        mImsManager.setEnhanced4gLteModeSetting(isChecked);
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+        if (imsMmTelManager == null) {
+            return false;
+        }
+        try {
+            imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked);
+        } catch (IllegalArgumentException exception) {
+            Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception);
+            return false;
+        }
         for (final On4gLteUpdateListener lsn : m4gLteListeners) {
             lsn.on4gLteUpdated();
         }
@@ -150,7 +172,8 @@
 
     @Override
     public boolean isChecked() {
-        return mImsManager.isEnhanced4gLteModeSettingEnabledByUser();
+        final VolteQueryImsState queryState = queryImsState(mSubId);
+        return queryState.isEnabledByUser();
     }
 
     public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) {
@@ -166,17 +189,19 @@
         return m4gCurrentMode == getMode();
     }
 
-    private boolean isPrefEnabled() {
-        return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
-                && (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE)
-                && mImsManager != null
-                && mImsManager.isNonTtyOrTtyOnVolteEnabled()
+    @VisibleForTesting
+    VolteQueryImsState queryImsState(int subId) {
+        return new VolteQueryImsState(mContext, subId);
+    }
+
+    private boolean isUserControlAllowed() {
+        return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE)
                 && mCarrierConfig.getBoolean(
                 CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL);
     }
 
     private boolean isVolteProvisionedOnDevice(int subId) {
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             return true;
         }
         final ProvisioningManager provisioningMgr = getProvisioningManager(subId);
@@ -204,7 +229,7 @@
 
         public void register(Context context, int subId) {
             mTelephonyManager = context.getSystemService(TelephonyManager.class);
-            if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
             }
             mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
diff --git a/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java b/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
index 9c5069b..3cad316 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
@@ -27,6 +27,7 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -43,17 +44,10 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.SliceBroadcastReceiver;
 
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
 /**
  * Helper class to control slices for enhanced 4g LTE settings.
  */
@@ -75,11 +69,6 @@
     public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY =
             "android.settings.NETWORK_OPERATOR_SETTINGS";
 
-    /**
-     * Timeout for querying enhanced 4g lte setting from ims manager.
-     */
-    private static final int TIMEOUT_MILLIS = 2000;
-
     private final Context mContext;
 
     /**
@@ -118,7 +107,7 @@
     public Slice createEnhanced4gLteSlice(Uri sliceUri) {
         final int subId = getDefaultVoiceSubId();
 
-        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.d(TAG, "Invalid subscription Id");
             return null;
         }
@@ -140,29 +129,16 @@
             return null;
         }
 
+        final VolteQueryImsState queryState = queryImsState(subId);
         try {
             return getEnhanced4gLteSlice(sliceUri,
-                    isEnhanced4gLteModeEnabled(imsManager), subId);
-        } catch (InterruptedException | TimeoutException | ExecutionException e) {
+                    queryState.isEnabledByUser(), subId);
+        } catch (IllegalArgumentException e) {
             Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e);
             return null;
         }
     }
 
-    private boolean isEnhanced4gLteModeEnabled(ImsManager imsManager)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        final FutureTask<Boolean> isEnhanced4gLteOnTask = new FutureTask<>(new Callable<Boolean>() {
-            @Override
-            public Boolean call() {
-                return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
-            }
-        });
-        final ExecutorService executor = Executors.newSingleThreadExecutor();
-        executor.execute(isEnhanced4gLteOnTask);
-
-        return isEnhanced4gLteOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
-    }
-
     /**
      * Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle
      * enables/disables Enhanced 4G LTE mode setting.
@@ -206,23 +182,45 @@
     public void handleEnhanced4gLteChanged(Intent intent) {
         final int subId = getDefaultVoiceSubId();
 
-        if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
             final ImsManager imsManager = getImsManager(subId);
             if (imsManager.isVolteEnabledByPlatform() && isVolteProvisionedOnDevice(subId)) {
-                final boolean currentValue = imsManager.isEnhanced4gLteModeSettingEnabledByUser()
-                        && imsManager.isNonTtyOrTtyOnVolteEnabled();
+                final VolteQueryImsState queryState = queryImsState(subId);
+                final boolean currentValue = queryState.isEnabledByUser()
+                        && queryState.isAllowUserControl();
                 final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
                         currentValue);
                 if (newValue != currentValue) {
-                    imsManager.setEnhanced4gLteModeSetting(newValue);
+                    setEnhanced4gLteModeSetting(subId, newValue);
                 }
             }
         }
+        notifyEnhanced4gLteUpdate();
+    }
+
+    private void notifyEnhanced4gLteUpdate() {
         // notify change in slice in any case to get re-queried. This would result in displaying
         // appropriate message with the updated setting.
         mContext.getContentResolver().notifyChange(CustomSliceRegistry.ENHANCED_4G_SLICE_URI, null);
     }
 
+    @VisibleForTesting
+    void setEnhanced4gLteModeSetting(int subId, boolean isEnabled) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return;
+        }
+        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
+        if (imsMmTelManager == null) {
+            return;
+        }
+        try {
+            imsMmTelManager.setAdvancedCallingSettingEnabled(isEnabled);
+        } catch (IllegalArgumentException exception) {
+            Log.w(TAG, "Unable to change the Enhanced 4g LTE to " + isEnabled + ". subId=" + subId,
+                    exception);
+        }
+    }
+
     private CharSequence getEnhanced4glteModeTitle(int subId) {
         CharSequence ret = mContext.getText(R.string.enhanced_4g_lte_mode_title);
         try {
@@ -296,5 +294,10 @@
                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
     }
+
+    @VisibleForTesting
+    VolteQueryImsState queryImsState(int subId) {
+        return new VolteQueryImsState(mContext, subId);
+    }
 }
 
diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java
index 40c747c..b76eb38 100644
--- a/src/com/android/settings/network/telephony/MobileDataSlice.java
+++ b/src/com/android/settings/network/telephony/MobileDataSlice.java
@@ -39,6 +39,7 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.network.MobileDataContentObserver;
+import com.android.settings.network.SubscriptionUtil;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.CustomSliceable;
 import com.android.settings.slices.SliceBackgroundWorker;
@@ -177,7 +178,7 @@
      */
     private boolean isMobileDataAvailable() {
         final List<SubscriptionInfo> subInfoList =
-                mSubscriptionManager.getSelectableSubscriptionInfoList();
+                SubscriptionUtil.getSelectableSubscriptionInfoList(mContext);
 
         return !(subInfoList == null || subInfoList.isEmpty());
     }
diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
index 37cfc07..2973020 100644
--- a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
@@ -22,9 +22,11 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -34,6 +36,8 @@
 import com.android.ims.ImsManager;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
+import com.android.settings.network.ims.VtQueryImsState;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -46,6 +50,8 @@
         MobileDataEnabledListener.Client,
         Enhanced4gBasePreferenceController.On4gLteUpdateListener {
 
+    private static final String TAG = "VideoCallingPreference";
+
     private Preference mPreference;
     private CarrierConfigManager mCarrierConfigManager;
     @VisibleForTesting
@@ -64,7 +70,7 @@
 
     @Override
     public int getAvailabilityStatus(int subId) {
-        return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+        return SubscriptionManager.isValidSubscriptionId(subId)
                 && isVideoCallEnabled(subId)
                 ? AVAILABLE
                 : CONDITIONALLY_UNAVAILABLE;
@@ -98,28 +104,41 @@
         final boolean videoCallEnabled = isVideoCallEnabled(mSubId, mImsManager);
         switchPreference.setVisible(videoCallEnabled);
         if (videoCallEnabled) {
-            final boolean is4gLteEnabled = mImsManager.isEnhanced4gLteModeSettingEnabledByUser()
+            final boolean videoCallEditable = queryVoLteState(mSubId).isEnabledByUser()
                     && mImsManager.isNonTtyOrTtyOnVolteEnabled();
-            preference.setEnabled(is4gLteEnabled &&
-                    mCallState == TelephonyManager.CALL_STATE_IDLE);
-            switchPreference.setChecked(is4gLteEnabled && mImsManager.isVtEnabledByUser());
+            preference.setEnabled(videoCallEditable
+                    && mCallState == TelephonyManager.CALL_STATE_IDLE);
+            switchPreference.setChecked(videoCallEditable && isChecked());
         }
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        mImsManager.setVtSetting(isChecked);
-        return true;
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return false;
+        }
+        final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+        if (imsMmTelManager == null) {
+            return false;
+        }
+        try {
+            imsMmTelManager.setVtSettingEnabled(isChecked);
+            return true;
+        } catch (IllegalArgumentException exception) {
+            Log.w(TAG, "Unable to set VT status " + isChecked + ". subId=" + mSubId,
+                    exception);
+        }
+        return false;
     }
 
     @Override
     public boolean isChecked() {
-        return mImsManager.isVtEnabledByUser();
+        return queryImsState(mSubId).isEnabledByUser();
     }
 
     public VideoCallingPreferenceController init(int subId) {
         mSubId = subId;
-        if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
             mImsManager = ImsManager.getInstance(mContext,
                     SubscriptionUtil.getPhoneId(mContext, mSubId));
         }
@@ -128,7 +147,7 @@
     }
 
     private boolean isVideoCallEnabled(int subId) {
-        final ImsManager imsManager = subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+        final ImsManager imsManager = SubscriptionManager.isValidSubscriptionId(subId)
                 ? ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(mContext, subId))
                 : null;
         return isVideoCallEnabled(subId, imsManager);
@@ -156,7 +175,7 @@
     boolean isVideoCallEnabled(int subId, ImsManager imsManager) {
         final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
         TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
-        if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
             telephonyManager = telephonyManager.createForSubscriptionId(subId);
         }
         return carrierConfig != null && imsManager != null
@@ -189,7 +208,7 @@
 
         public void register(Context context, int subId) {
             mTelephonyManager = context.getSystemService(TelephonyManager.class);
-            if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                 mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
             }
             mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
@@ -207,4 +226,14 @@
     public void onMobileDataEnabledChange() {
         updateState(mPreference);
     }
+
+    @VisibleForTesting
+    VtQueryImsState queryImsState(int subId) {
+        return new VtQueryImsState(mContext, subId);
+    }
+
+    @VisibleForTesting
+    VolteQueryImsState queryVoLteState(int subId) {
+        return new VolteQueryImsState(mContext, subId);
+    }
 }
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
index 9c9bf2f..ec23465 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
@@ -37,6 +37,7 @@
 import com.android.ims.ImsManager;
 import com.android.settings.R;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -49,6 +50,8 @@
 public class WifiCallingPreferenceController extends TelephonyBasePreferenceController implements
         LifecycleObserver, OnStart, OnStop {
 
+    private static final String TAG = "WifiCallingPreference";
+
     @VisibleForTesting
     Integer mCallState;
     @VisibleForTesting
@@ -69,7 +72,7 @@
 
     @Override
     public int getAvailabilityStatus(int subId) {
-        return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+        return SubscriptionManager.isValidSubscriptionId(subId)
                 && MobileNetworkUtils.isWifiCallingEnabled(mContext, subId)
                 ? AVAILABLE
                 : UNSUPPORTED_ON_DEVICE;
@@ -101,6 +104,7 @@
         if (mCallState == null) {
             return;
         }
+        CharSequence summaryText = null;
         if (mSimCallManager != null) {
             final Intent intent = MobileNetworkUtils.buildPhoneAccountConfigureIntent(mContext,
                     mSimCallManager);
@@ -111,48 +115,51 @@
             final PackageManager pm = mContext.getPackageManager();
             final List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
             preference.setTitle(resolutions.get(0).loadLabel(pm));
-            preference.setSummary(null);
             preference.setIntent(intent);
         } else {
             final String title = SubscriptionManager.getResourcesForSubId(mContext, mSubId)
                     .getString(R.string.wifi_calling_settings_title);
             preference.setTitle(title);
-            int resId = com.android.internal.R.string.wifi_calling_off_summary;
-            if (mImsManager.isWfcEnabledByUser()) {
-                boolean useWfcHomeModeForRoaming = false;
-                if (mCarrierConfigManager != null) {
-                    final PersistableBundle carrierConfig =
-                            mCarrierConfigManager.getConfigForSubId(mSubId);
-                    if (carrierConfig != null) {
-                        useWfcHomeModeForRoaming = carrierConfig.getBoolean(
-                                CarrierConfigManager
-                                        .KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL);
-                    }
-                }
-                final boolean isRoaming = getTelephonyManager(mContext, mSubId)
-                        .isNetworkRoaming();
-                final int wfcMode = (isRoaming && !useWfcHomeModeForRoaming)
-                        ? mImsMmTelManager.getVoWiFiRoamingModeSetting() :
-                        mImsMmTelManager.getVoWiFiModeSetting();
-                switch (wfcMode) {
-                    case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
-                        resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
-                        break;
-                    case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
-                        resId = com.android.internal.R.string
-                                .wfc_mode_cellular_preferred_summary;
-                        break;
-                    case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
-                        resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
-                        break;
-                    default:
-                        break;
+            summaryText = getResourceIdForWfcMode(mSubId);
+        }
+        preference.setSummary(summaryText);
+        preference.setEnabled(mCallState == TelephonyManager.CALL_STATE_IDLE);
+    }
+
+    private CharSequence getResourceIdForWfcMode(int subId) {
+        int resId = com.android.internal.R.string.wifi_calling_off_summary;
+        if (queryImsState(subId).isEnabledByUser()) {
+            boolean useWfcHomeModeForRoaming = false;
+            if (mCarrierConfigManager != null) {
+                final PersistableBundle carrierConfig =
+                        mCarrierConfigManager.getConfigForSubId(subId);
+                if (carrierConfig != null) {
+                    useWfcHomeModeForRoaming = carrierConfig.getBoolean(
+                            CarrierConfigManager
+                                    .KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL);
                 }
             }
-            preference.setSummary(
-                    SubscriptionManager.getResourcesForSubId(mContext, mSubId).getText(resId));
+            final boolean isRoaming = getTelephonyManager(mContext, subId)
+                    .isNetworkRoaming();
+            final int wfcMode = (isRoaming && !useWfcHomeModeForRoaming)
+                    ? mImsMmTelManager.getVoWiFiRoamingModeSetting() :
+                    mImsMmTelManager.getVoWiFiModeSetting();
+            switch (wfcMode) {
+                case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
+                    resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
+                    break;
+                case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
+                    resId = com.android.internal.R.string
+                            .wfc_mode_cellular_preferred_summary;
+                    break;
+                case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
+                    resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
+                    break;
+                default:
+                    break;
+            }
         }
-        preference.setEnabled(mCallState == TelephonyManager.CALL_STATE_IDLE);
+        return SubscriptionManager.getResourcesForSubId(mContext, subId).getText(resId);
     }
 
     public WifiCallingPreferenceController init(int subId) {
@@ -166,14 +173,22 @@
         return this;
     }
 
+    @VisibleForTesting
+    WifiCallingQueryImsState queryImsState(int subId) {
+        return new WifiCallingQueryImsState(mContext, subId);
+    }
+
     protected ImsMmTelManager getImsMmTelManager(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return null;
+        }
         return ImsMmTelManager.createForSubscriptionId(subId);
     }
 
     @VisibleForTesting
     TelephonyManager getTelephonyManager(Context context, int subId) {
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             return telephonyMgr;
         }
         final TelephonyManager subscriptionTelephonyMgr =
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index a8396a1..f749764 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -21,6 +21,7 @@
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
+import android.app.NotificationHistory;
 import android.app.role.RoleManager;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
@@ -367,6 +368,15 @@
         return false;
     }
 
+    public NotificationHistory getNotificationHistory(String pkg) {
+        try {
+            return sINM.getNotificationHistory(pkg);
+        } catch (Exception e) {
+            Log.w(TAG, "Error calling NoMan", e);
+        }
+        return new NotificationHistory();
+    }
+
     protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
         long now = System.currentTimeMillis();
         long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
diff --git a/src/com/android/settings/notification/history/HistoryLoader.java b/src/com/android/settings/notification/history/HistoryLoader.java
new file mode 100644
index 0000000..046b5ef
--- /dev/null
+++ b/src/com/android/settings/notification/history/HistoryLoader.java
@@ -0,0 +1,93 @@
+/*
+ * 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.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HistoryLoader {
+    private final Context mContext;
+    private final NotificationBackend mBackend;
+    private final PackageManager mPm;
+
+    public HistoryLoader(Context context, NotificationBackend backend, PackageManager pm) {
+        mContext = context;
+        mBackend = backend;
+        mPm = pm;
+    }
+
+    public void load(OnHistoryLoaderListener listener) {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            Map<String, NotificationHistoryPackage> historicalNotifications = new HashMap<>();
+            NotificationHistory history =
+                    mBackend.getNotificationHistory(mContext.getPackageName());
+
+            while (history.hasNextNotification()) {
+                HistoricalNotification hn = history.getNextNotification();
+
+                String key = hn.getPackage() + "|" + hn.getUid();
+                NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault(
+                        key,
+                        new NotificationHistoryPackage(hn.getPackage(), hn.getUid()));
+                hnsForPackage.notifications.add(hn);
+                historicalNotifications.put(key, hnsForPackage);
+            }
+            List<NotificationHistoryPackage> packages =
+                    new ArrayList<>(historicalNotifications.values());
+            Collections.sort(packages,
+                    (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent()));
+            for (NotificationHistoryPackage nhp : packages) {
+                ApplicationInfo info;
+                try {
+                    info = mPm.getApplicationInfoAsUser(
+                            nhp.pkgName,
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                    | PackageManager.MATCH_DISABLED_COMPONENTS
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+                            UserHandle.getUserId(nhp.uid));
+                    if (info != null) {
+                        nhp.label = String.valueOf(mPm.getApplicationLabel(info));
+                        nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info),
+                                UserHandle.of(UserHandle.getUserId(nhp.uid)));
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // app is gone, just show package name and generic icon
+                    nhp.icon = mPm.getDefaultActivityIcon();
+                }
+            }
+            ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages));
+        });
+    }
+
+    interface OnHistoryLoaderListener {
+        void onHistoryLoaded(List<NotificationHistoryPackage> notificationsByPackage);
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
new file mode 100644
index 0000000..173508b
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -0,0 +1,156 @@
+/*
+ * 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.history;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.INotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+
+import java.util.Arrays;
+
+public class NotificationHistoryActivity extends Activity {
+
+    private static String TAG = "NotifHistory";
+
+    private ViewGroup mTodayView;
+    private ViewGroup mSnoozeView;
+    private ViewGroup mDismissView;
+    private HistoryLoader mHistoryLoader;
+    private INotificationManager mNm;
+    private PackageManager mPm;
+
+    private HistoryLoader.OnHistoryLoaderListener mOnHistoryLoaderListener = notifications -> {
+        // for each package, new header and recycler view
+        for (NotificationHistoryPackage nhp : notifications) {
+            View viewForPackage = LayoutInflater.from(this)
+                    .inflate(R.layout.notification_history_app_layout, null);
+
+            final View container = viewForPackage.findViewById(R.id.list_container);
+            container.setVisibility(View.GONE);
+            ImageButton expand = viewForPackage.findViewById(R.id.expand);
+            expand.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);
+            });
+
+            TextView label = viewForPackage.findViewById(R.id.label);
+            label.setText(nhp.label != null ? nhp.label : nhp.pkgName);
+            ImageView icon = viewForPackage.findViewById(R.id.icon);
+            icon.setImageDrawable(nhp.icon);
+
+            RecyclerView rv = viewForPackage.findViewById(R.id.notification_list);
+            rv.setLayoutManager(new LinearLayoutManager(this));
+            rv.setAdapter(new NotificationHistoryAdapter());
+            ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete(nhp.notifications);
+            mTodayView.addView(viewForPackage);
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.notification_history);
+        mTodayView = findViewById(R.id.apps);
+        mSnoozeView = findViewById(R.id.snoozed_list);
+        mDismissView = findViewById(R.id.recently_dismissed_list);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mPm = getPackageManager();
+
+        mHistoryLoader = new HistoryLoader(this, new NotificationBackend(), mPm);
+        mHistoryLoader.load(mOnHistoryLoaderListener);
+
+        mNm = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        try {
+            mListener.registerAsSystemService(this, new ComponentName(getPackageName(),
+                    this.getClass().getCanonicalName()), ActivityManager.getCurrentUser());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot register listener", e);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        try {
+            mListener.unregisterAsSystemService();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Cannot unregister listener", e);
+        }
+        super.onPause();
+    }
+
+    private final NotificationListenerService mListener = new NotificationListenerService() {
+
+        @Override
+        public void onListenerConnected() {
+            StatusBarNotification[] snoozed = getSnoozedNotifications();
+            if (snoozed == null || snoozed.length == 0) {
+                mSnoozeView.setVisibility(View.GONE);
+            } else {
+                RecyclerView rv = mSnoozeView.findViewById(R.id.notification_list);
+                rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+                rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+                ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+                        Arrays.asList(snoozed));
+            }
+
+            try {
+                StatusBarNotification[] dismissed = mNm.getHistoricalNotifications(
+                        NotificationHistoryActivity.this.getPackageName(), 10);
+                RecyclerView rv = mDismissView.findViewById(R.id.notification_list);
+                rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+                rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+                ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+                        Arrays.asList(dismissed));
+                mDismissView.setVisibility(View.VISIBLE);
+            } catch (Exception e) {
+                Slog.e(TAG, "Cannot load recently dismissed", e);
+                mDismissView.setVisibility(View.GONE);
+            }
+        }
+    };
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
new file mode 100644
index 0000000..f87bb20
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * 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.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class NotificationHistoryAdapter extends
+        RecyclerView.Adapter<NotificationHistoryViewHolder> {
+
+    private List<HistoricalNotification> mValues;
+
+    public NotificationHistoryAdapter() {
+        mValues = new ArrayList<>();
+        setHasStableIds(true);
+    }
+
+    @Override
+    public NotificationHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.notification_history_log_row, parent, false);
+        return new NotificationHistoryViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder,
+            int position) {
+        final HistoricalNotification hn = mValues.get(position);
+        holder.setTitle(hn.getTitle());
+        holder.setSummary(hn.getText());
+        holder.setPostedTime(hn.getPostedTimeMs());
+        holder.addOnClick(hn.getPackage(), hn.getUserId(), hn.getChannelId());
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+
+    public void onRebuildComplete(List<HistoricalNotification> notifications) {
+        mValues = notifications;
+        notifyDataSetChanged();
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryPackage.java b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
new file mode 100644
index 0000000..5170b75
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
@@ -0,0 +1,62 @@
+/*
+ * 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.history;
+
+import android.app.NotificationHistory;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class NotificationHistoryPackage {
+    String pkgName;
+    int uid;
+    List<NotificationHistory.HistoricalNotification> notifications;
+    CharSequence label;
+    Drawable icon;
+
+    public NotificationHistoryPackage(String pkgName, int uid) {
+        this.pkgName = pkgName;
+        this.uid = uid;
+        notifications = new ArrayList<>();
+    }
+
+    public long getMostRecent() {
+        if (notifications.isEmpty()) {
+            return 0;
+        }
+        return notifications.get(0).getPostedTimeMs();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotificationHistoryPackage that = (NotificationHistoryPackage) o;
+        return uid == that.uid &&
+                Objects.equals(pkgName, that.pkgName) &&
+                Objects.equals(notifications, that.notifications) &&
+                Objects.equals(label, that.label) &&
+                Objects.equals(icon, that.icon);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(pkgName, uid, notifications, label, icon);
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
new file mode 100644
index 0000000..35f5615
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
@@ -0,0 +1,70 @@
+/*
+ * 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.history;
+
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationHistoryViewHolder extends RecyclerView.ViewHolder {
+
+    private final DateTimeView mTime;
+    private final TextView mTitle;
+    private final TextView mSummary;
+
+    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);
+    }
+
+    void setSummary(CharSequence summary) {
+        mSummary.setText(summary);
+        mSummary.setVisibility(summary != null ? View.VISIBLE : View.GONE);
+    }
+
+    void setTitle(CharSequence title) {
+        mTitle.setText(title);
+        mTitle.setVisibility(title != null ? View.VISIBLE : View.GONE);
+    }
+
+    void setPostedTime(long postedTime) {
+        mTime.setTime(postedTime);
+    }
+
+    void addOnClick(String pkg, int userId, String channelId) {
+        itemView.setOnClickListener(v -> {
+            Intent intent =  new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+                    .putExtra(EXTRA_APP_PACKAGE, pkg)
+                    .putExtra(EXTRA_CHANNEL_ID, channelId);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            itemView.getContext().startActivityAsUser(intent, UserHandle.of(userId));
+        });
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
new file mode 100644
index 0000000..80ba278
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
@@ -0,0 +1,152 @@
+/*
+ * 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.history;
+
+import static android.content.pm.PackageManager.*;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class NotificationSbnAdapter extends
+        RecyclerView.Adapter<NotificationSbnViewHolder> {
+
+    private static final String TAG = "SbnAdapter";
+    private List<StatusBarNotification> mValues;
+    private Map<Integer, Drawable> mUserBadgeCache;
+    private final Context mContext;
+    private PackageManager mPm;
+
+    public NotificationSbnAdapter(Context context, PackageManager pm) {
+        mContext = context;
+        mPm = pm;
+        mUserBadgeCache = new HashMap<>();
+        mValues = new ArrayList<>();
+        setHasStableIds(true);
+    }
+
+    @Override
+    public NotificationSbnViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.notification_sbn_log_row, parent, false);
+        return new NotificationSbnViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final @NonNull NotificationSbnViewHolder holder,
+            int position) {
+        final StatusBarNotification sbn = mValues.get(position);
+        holder.setIcon(loadIcon(sbn));
+        holder.setPackageName(loadPackageName(sbn.getPackageName()).toString());
+        holder.setTitle(getTitleString(sbn.getNotification()));
+        holder.setSummary(getTextString(mContext, sbn.getNotification()));
+        holder.setPostedTime(sbn.getPostTime());
+        if (!mUserBadgeCache.containsKey(sbn.getUserId())) {
+            Drawable profile = mContext.getPackageManager().getUserBadgeForDensity(
+                    UserHandle.of(sbn.getUserId()), -1);
+            mUserBadgeCache.put(sbn.getUserId(), profile);
+        }
+        holder.setProfileBadge(mUserBadgeCache.get(sbn.getUserId()));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+
+    public void onRebuildComplete(List<StatusBarNotification> notifications) {
+        mValues = notifications;
+        notifyDataSetChanged();
+    }
+
+    private @NonNull CharSequence loadPackageName(String pkg) {
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(pkg,
+                    MATCH_ANY_USER);
+            if (info != null) return mPm.getApplicationLabel(info);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Cannot load package name", e);
+        }
+        return pkg;
+    }
+
+    private static String getTitleString(Notification n) {
+        CharSequence title = null;
+        if (n.extras != null) {
+            title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
+        }
+        return title == null? null : String.valueOf(title);
+    }
+
+    /**
+     * Returns the appropriate substring for this notification based on the style of notification.
+     */
+    private static String getTextString(Context appContext, Notification n) {
+        CharSequence text = null;
+        if (n.extras != null) {
+            text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+
+            Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+
+            if (nb.getStyle() instanceof Notification.BigTextStyle) {
+                text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
+            } else if (nb.getStyle() instanceof Notification.MessagingStyle) {
+                Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
+                final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
+                if (messages != null && messages.size() > 0) {
+                    text = messages.get(messages.size() - 1).getText();
+                }
+            }
+
+            if (TextUtils.isEmpty(text)) {
+                text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+            }
+        }
+        return text == null ? null : String.valueOf(text);
+    }
+
+    private Drawable loadIcon(StatusBarNotification sbn) {
+        Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
+                sbn.getPackageContext(mContext), sbn.getUserId());
+        if (draw == null) {
+            return null;
+        }
+        draw.mutate();
+        draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
+        return draw;
+    }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
new file mode 100644
index 0000000..79cda7f
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnViewHolder.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.notification.history;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationSbnViewHolder extends RecyclerView.ViewHolder {
+
+    private final TextView mPkgName;
+    private final ImageView mIcon;
+    private final DateTimeView mTime;
+    private final TextView mTitle;
+    private final TextView mSummary;
+    private final ImageView mProfileBadge;
+
+    NotificationSbnViewHolder(View itemView) {
+        super(itemView);
+        mPkgName = itemView.findViewById(R.id.pkgname);
+        mIcon = itemView.findViewById(R.id.icon);
+        mTime = itemView.findViewById(R.id.timestamp);
+        mTitle = itemView.findViewById(R.id.title);
+        mSummary = itemView.findViewById(R.id.text);
+        mProfileBadge = itemView.findViewById(R.id.profile_badge);
+    }
+
+    void setSummary(CharSequence summary) {
+        mSummary.setVisibility(TextUtils.isEmpty(summary) ? View.GONE : View.VISIBLE);
+        mSummary.setText(summary);
+    }
+
+    void setTitle(CharSequence title) {
+        if (title == null) {
+            return;
+        }
+        mTitle.setText(title);
+    }
+
+    void setIcon(Drawable icon) {
+        mIcon.setImageDrawable(icon);
+    }
+
+    void setPackageName(String pkg) {
+        mPkgName.setText(pkg);
+    }
+
+    void setPostedTime(long postedTime) {
+        mTime.setTime(postedTime);
+    }
+
+    void setProfileBadge(Drawable badge) {
+        mProfileBadge.setImageDrawable(badge);
+    }
+}
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
index de3dc89..badaeb1 100644
--- a/src/com/android/settings/panel/PanelContent.java
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -28,11 +28,13 @@
  */
 public interface PanelContent extends Instrumentable {
 
+    int ICON_UNAVAILABLE = -1;
+
     /**
      * @return a icon resource for the title of the Panel.
      */
     default int getIcon() {
-        return -1;
+        return ICON_UNAVAILABLE;
     }
 
     /**
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index db01a28..40706fe 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -193,7 +193,7 @@
         loadAllSlices();
 
         final int iconRes = mPanel.getIcon();
-        if (iconRes == -1) {
+        if (iconRes == PanelContent.ICON_UNAVAILABLE) {
             mTitleView.setText(mPanel.getTitle());
         } else {
             mTitleView.setVisibility(View.GONE);
diff --git a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
index fb8d85e..b584ea1 100644
--- a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
+++ b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
@@ -53,15 +53,20 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * The Fragment list those networks, which is proposed by other app, to user, and handle user's
  * choose on either saving those networks or rejecting the request.
  */
-public class AddAppNetworksFragment extends InstrumentedFragment {
+public class AddAppNetworksFragment extends InstrumentedFragment implements
+        WifiTracker.WifiListener {
     public static final String TAG = "AddAppNetworksFragment";
 
     // Possible result values in each item of the returned result list, which is used
@@ -78,8 +83,9 @@
     private static final int MESSAGE_SHOW_SAVE_FAILED = 3;
     private static final int MESSAGE_FINISH = 4;
 
-    // Signal level for the constant signal icon.
-    private static final int MAX_RSSI_SIGNAL_LEVEL = 4;
+    // Signal level for the initial signal icon.
+    @VisibleForTesting
+    static final int INITIAL_RSSI_SIGNAL_LEVEL = 0;
     // Max networks count within one request
     private static final int MAX_SPECIFIC_NETWORKS_COUNT = 5;
 
@@ -103,6 +109,8 @@
     List<UiConfigurationItem> mUiToRequestedList;
     @VisibleForTesting
     List<Integer> mResultCodeArrayList;
+    @VisibleForTesting
+    WifiTracker mWifiTracker;
 
     private boolean mIsSingleNetwork;
     private boolean mAnyNetworkSavedSuccess;
@@ -156,6 +164,8 @@
             @Nullable Bundle savedInstanceState) {
         mActivity = getActivity();
         mWifiManager = mActivity.getSystemService(WifiManager.class);
+        mWifiTracker = WifiTrackerFactory.create(mActivity.getApplication(), this,
+                getSettingsLifecycle(), true /* includeSaved */, true /* includeScans */);
 
         return inflater.inflate(R.layout.wifi_add_app_networks, container, false);
     }
@@ -223,7 +233,7 @@
             mLayoutView.findViewById(R.id.single_network).setVisibility(View.VISIBLE);
 
             // Show signal icon for single network case.
-            setSingleNetworkSignalIcon();
+            updateSingleNetworkSignalIcon(INITIAL_RSSI_SIGNAL_LEVEL);
             // Show the SSID of the proposed network.
             ((TextView) mLayoutView.findViewById(R.id.single_ssid)).setText(
                     mUiToRequestedList.get(0).mDisplayedSsid);
@@ -323,61 +333,51 @@
      * networks, for creating UI to user.
      */
     @VisibleForTesting
-    void filterSavedNetworks(
-            List<WifiConfiguration> savedWifiConfigurations) {
+    void filterSavedNetworks(List<WifiConfiguration> savedWifiConfigurations) {
         if (mUiToRequestedList == null) {
             mUiToRequestedList = new ArrayList<>();
         } else {
             mUiToRequestedList.clear();
         }
 
-        boolean foundInSavedList;
-        boolean foundError;
-        String displayedName = null;
         int networkPositionInBundle = 0;
         for (WifiNetworkSuggestion suggestion : mAllSpecifiedNetworksList) {
-            foundInSavedList = false;
-            foundError = false;
+            String displayedName = null;
+            boolean foundInSavedList = false;
 
-            /**
+            /*
              * If specified is passpoint network, need to check with the existing passpoint
              * networks.
              */
-            if (suggestion.passpointConfiguration != null) {
-                if (!suggestion.passpointConfiguration.validate()) {
-                    foundError = true;
-                } else {
-                    foundInSavedList = isSavedPasspointConfiguration(
-                            suggestion.passpointConfiguration);
-                    displayedName = suggestion.passpointConfiguration.getHomeSp().getFriendlyName();
-                }
+            final PasspointConfiguration passpointConfig = suggestion.getPasspointConfiguration();
+            if (passpointConfig != null) {
+                foundInSavedList = isSavedPasspointConfiguration(passpointConfig);
+                displayedName = passpointConfig.getHomeSp().getFriendlyName();
             } else {
-                final WifiConfiguration specifiedConfig = suggestion.wifiConfiguration;
+                final WifiConfiguration specifiedConfig = suggestion.getWifiConfiguration();
                 displayedName = removeDoubleQuotes(specifiedConfig.SSID);
                 foundInSavedList = isSavedWifiConfiguration(specifiedConfig,
                         savedWifiConfigurations);
             }
 
-            if (foundError) {
-                mResultCodeArrayList.set(networkPositionInBundle, RESULT_NETWORK_ADD_ERROR);
-            } else if (foundInSavedList) {
+            if (foundInSavedList) {
                 // If this requested network already in the saved networks, mark this item in the
                 // result code list as existed.
                 mResultCodeArrayList.set(networkPositionInBundle, RESULT_NETWORK_ALREADY_EXISTS);
             } else {
                 // Prepare to add to UI list to show to user
-                UiConfigurationItem uiConfigurationIcon = new UiConfigurationItem(displayedName,
-                        suggestion, networkPositionInBundle);
-                mUiToRequestedList.add(uiConfigurationIcon);
+                UiConfigurationItem uiConfigurationItem = new UiConfigurationItem(displayedName,
+                        suggestion, networkPositionInBundle, INITIAL_RSSI_SIGNAL_LEVEL);
+                mUiToRequestedList.add(uiConfigurationItem);
             }
             networkPositionInBundle++;
         }
     }
 
-    private void setSingleNetworkSignalIcon() {
+    private void updateSingleNetworkSignalIcon(int level) {
         // TODO: Check level of the network to show signal icon.
         final Drawable wifiIcon = mActivity.getDrawable(
-                Utils.getWifiIconResource(MAX_RSSI_SIGNAL_LEVEL)).mutate();
+                Utils.getWifiIconResource(level)).mutate();
         final Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
         wifiIconDark.setTintList(
                 Utils.getColorAttr(mActivity, android.R.attr.colorControlNormal));
@@ -472,12 +472,14 @@
         public final String mDisplayedSsid;
         public final WifiNetworkSuggestion mWifiNetworkSuggestion;
         public final int mIndex;
+        public int mLevel;
 
         UiConfigurationItem(String displayedSsid, WifiNetworkSuggestion wifiNetworkSuggestion,
-                int index) {
+                int index, int level) {
             mDisplayedSsid = displayedSsid;
             mWifiNetworkSuggestion = wifiNetworkSuggestion;
             mIndex = index;
+            mLevel = level;
         }
     }
 
@@ -515,7 +517,8 @@
             final PreferenceImageView imageView = view.findViewById(android.R.id.icon);
             if (imageView != null) {
                 final Drawable drawable = getContext().getDrawable(
-                        com.android.settingslib.Utils.getWifiIconResource(MAX_RSSI_SIGNAL_LEVEL));
+                        com.android.settingslib.Utils.getWifiIconResource(
+                                uiConfigurationItem.mLevel));
                 drawable.setTintList(
                         com.android.settingslib.Utils.getColorAttr(getContext(),
                                 android.R.attr.colorControlNormal));
@@ -596,11 +599,12 @@
      * Call framework API to save single network.
      */
     private void saveNetwork(int index) {
-        if (mUiToRequestedList.get(index).mWifiNetworkSuggestion.passpointConfiguration != null) {
+        final PasspointConfiguration passpointConfig =
+                mUiToRequestedList.get(index).mWifiNetworkSuggestion.getPasspointConfiguration();
+        if (passpointConfig != null) {
             // Save passpoint, if no IllegalArgumentException, then treat it as success.
             try {
-                mWifiManager.addOrUpdatePasspointConfiguration(mUiToRequestedList.get(
-                        index).mWifiNetworkSuggestion.passpointConfiguration);
+                mWifiManager.addOrUpdatePasspointConfiguration(passpointConfig);
                 mAnyNetworkSavedSuccess = true;
             } catch (IllegalArgumentException e) {
                 mResultCodeArrayList.set(mUiToRequestedList.get(index).mIndex,
@@ -612,18 +616,17 @@
             }
             // Show saved or failed according to all results.
             showSavedOrFail();
-            return;
         } else {
-            final WifiConfiguration wifiConfiguration = mUiToRequestedList.get(
-                    index).mWifiNetworkSuggestion.wifiConfiguration;
+            final WifiConfiguration wifiConfiguration =
+                    mUiToRequestedList.get(index).mWifiNetworkSuggestion.getWifiConfiguration();
             wifiConfiguration.SSID = addQuotationIfNeeded(wifiConfiguration.SSID);
             mWifiManager.save(wifiConfiguration, mSaveListener);
         }
     }
 
     private void connectNetwork(int index) {
-        final WifiConfiguration wifiConfiguration = mUiToRequestedList.get(
-                index).mWifiNetworkSuggestion.wifiConfiguration;
+        final WifiConfiguration wifiConfiguration =
+                mUiToRequestedList.get(index).mWifiNetworkSuggestion.getWifiConfiguration();
         mWifiManager.connect(wifiConfiguration, null /* ActionListener */);
     }
 
@@ -700,4 +703,46 @@
                 break;
         }
     }
+
+    @Override
+    public void onWifiStateChanged(int state) {
+        // Do nothing
+    }
+
+    @Override
+    public void onConnectedChanged() {
+        // Do nothing
+    }
+
+    @VisibleForTesting
+    void updateScanResults(List<AccessPoint> allAccessPoints) {
+        if (mUiToRequestedList == null) {
+            // Nothing need to be updated.
+            return;
+        }
+
+        // Update the signal level of the UI networks.
+        for (UiConfigurationItem uiConfigurationItem : mUiToRequestedList) {
+            final Optional<AccessPoint> matchedAccessPoint = allAccessPoints
+                    .stream()
+                    .filter(accesspoint -> accesspoint.matches(
+                            uiConfigurationItem.mWifiNetworkSuggestion.getWifiConfiguration()))
+                    .findFirst();
+            uiConfigurationItem.mLevel =
+                    matchedAccessPoint.isPresent() ? matchedAccessPoint.get().getLevel() : 0;
+        }
+
+        if (mIsSingleNetwork) {
+            updateSingleNetworkSignalIcon(mUiToRequestedList.get(0).mLevel);
+        } else {
+            if (mUiConfigurationItemAdapter != null) {
+                mUiConfigurationItemAdapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    public void onAccessPointsChanged() {
+        updateScanResults(mWifiTracker.getAccessPoints());
+    }
 }
diff --git a/src/com/android/settings/wifi/calling/OWNERS b/src/com/android/settings/wifi/calling/OWNERS
new file mode 100644
index 0000000..d23f816
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/OWNERS
@@ -0,0 +1,8 @@
+# Default reviewers for this and subdirectories.
+allenwtsu@google.com
+andychou@google.com
+bonianchen@google.com
+songferngwang@google.com
+tomhsu@google.com
+
+# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index ea34fce..cc9c22f 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -46,7 +46,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.ims.ImsConfig;
-import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
@@ -56,6 +55,7 @@
 import com.android.settings.Utils;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 import com.android.settings.widget.SwitchBar;
 
@@ -102,6 +102,7 @@
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ImsManager mImsManager;
     private ImsMmTelManager mImsMmTelManager;
+    private ProvisioningManager mProvisioningManager;
     private TelephonyManager mTelephonyManager;
 
     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@@ -114,7 +115,8 @@
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             final SettingsActivity activity = (SettingsActivity) getActivity();
-            final boolean isNonTtyOrTtyOnVolteEnabled = mImsManager.isNonTtyOrTtyOnVolteEnabled();
+            final boolean isNonTtyOrTtyOnVolteEnabled =
+                    queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl();
             final boolean isWfcEnabled = mSwitchBar.isChecked()
                     && isNonTtyOrTtyOnVolteEnabled;
             boolean isCallStateIdle =
@@ -246,6 +248,19 @@
     }
 
     @VisibleForTesting
+    WifiCallingQueryImsState queryImsState(int subId) {
+        return new WifiCallingQueryImsState(getContext(), subId);
+    }
+
+    @VisibleForTesting
+    ProvisioningManager getImsProvisioningManager() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return null;
+        }
+        return ProvisioningManager.createForSubscriptionId(mSubId);
+    }
+
+    @VisibleForTesting
     ImsManager getImsManager() {
         return ImsManager.getInstance(getActivity(),
                 SubscriptionUtil.getPhoneId(getActivity(), mSubId));
@@ -253,6 +268,9 @@
 
     @VisibleForTesting
     ImsMmTelManager getImsMmTelManager() {
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            return null;
+        }
         return ImsMmTelManager.createForSubscriptionId(mSubId);
     }
 
@@ -272,9 +290,10 @@
         }
 
         mImsManager = getImsManager();
+        mProvisioningManager = getImsProvisioningManager();
         mImsMmTelManager = getImsMmTelManager();
 
-        mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
+        mTelephonyManager = getActivity().getSystemService(TelephonyManager.class);
 
         mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
         mButtonWfcMode.setOnPreferenceChangeListener(this);
@@ -342,7 +361,7 @@
             }
         }
 
-        Resources res = getResourcesForSubId();
+        final Resources res = getResourcesForSubId();
         mButtonWfcMode.setTitle(res.getString(R.string.wifi_calling_mode_title));
         mButtonWfcMode.setDialogTitle(res.getString(R.string.wifi_calling_mode_dialog_title));
         mButtonWfcRoamingMode.setTitle(res.getString(R.string.wifi_calling_roaming_mode_title));
@@ -383,8 +402,9 @@
         }
 
         // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
-        final boolean wfcEnabled = mImsManager.isWfcEnabledByUser()
-                && mImsManager.isNonTtyOrTtyOnVolteEnabled();
+        final WifiCallingQueryImsState queryIms = queryImsState(mSubId);
+        final boolean wfcEnabled = queryIms.isEnabledByUser()
+                && queryIms.isAllowUserControl();
         mSwitch.setChecked(wfcEnabled);
         final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
         final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
@@ -397,8 +417,6 @@
     public void onResume() {
         super.onResume();
 
-        final Context context = getActivity();
-
         updateBody();
 
         if (mImsManager.isWfcEnabledByPlatform()) {
@@ -409,6 +427,7 @@
             mValidListener = true;
         }
 
+        final Context context = getActivity();
         context.registerReceiver(mIntentReceiver, mIntentFilter);
 
         final Intent intent = getActivity().getIntent();
@@ -417,12 +436,7 @@
         }
 
         // Register callback for provisioning changes.
-        try {
-            mImsManager.getConfigInterface().addConfigCallback(mProvisioningCallback);
-        } catch (ImsException e) {
-            Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
-        }
-
+        registerProvisioningChangedCallback();
     }
 
     @Override
@@ -442,13 +456,7 @@
         context.unregisterReceiver(mIntentReceiver);
 
         // Remove callback for provisioning changes.
-        try {
-            mImsManager.getConfigInterface().removeConfigCallback(
-                    mProvisioningCallback.getBinder());
-        } catch (ImsException e) {
-            Log.w(TAG, "onPause: Unable to remove callback for provisioning changes");
-        }
-
+        unregisterProvisioningChangedCallback();
     }
 
     /**
@@ -508,7 +516,7 @@
      */
     private void updateWfcMode(boolean wfcEnabled) {
         Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
-        mImsManager.setWfcSetting(wfcEnabled);
+        mImsMmTelManager.setVoWiFiSettingEnabled(wfcEnabled);
 
         final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
         final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
@@ -524,8 +532,6 @@
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
 
-        final Context context = getActivity();
-
         Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
 
         switch (requestCode) {
@@ -619,7 +625,7 @@
 
     private CharSequence getWfcModeSummary(int wfcMode) {
         int resId = com.android.internal.R.string.wifi_calling_off_summary;
-        if (mImsManager.isWfcEnabledByUser()) {
+        if (queryImsState(mSubId).isEnabledByUser()) {
             switch (wfcMode) {
                 case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
                     resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
@@ -641,4 +647,25 @@
     Resources getResourcesForSubId() {
         return SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
     }
+
+    @VisibleForTesting
+    void registerProvisioningChangedCallback() {
+        if (mProvisioningManager == null) {
+            return;
+        }
+        try {
+            mProvisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+                    mProvisioningCallback);
+        } catch (Exception ex) {
+            Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
+        }
+    }
+
+    @VisibleForTesting
+    void unregisterProvisioningChangedCallback() {
+        if (mProvisioningManager == null) {
+            return;
+        }
+        mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback);
+    }
 }
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
index 8ca9d5f..895bdba 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
@@ -47,6 +47,7 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 import com.android.settings.slices.SliceBroadcastReceiver;
 
@@ -133,9 +134,8 @@
      */
     public Slice createWifiCallingSlice(Uri sliceUri) {
         final int subId = getDefaultVoiceSubId();
-        Resources res = getResourcesForSubId(subId);
 
-        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.d(TAG, "Invalid subscription Id");
             return null;
         }
@@ -148,42 +148,29 @@
             return null;
         }
 
-        try {
-            final boolean isWifiCallingEnabled = isWifiCallingEnabled(imsManager);
-            final Intent activationAppIntent =
-                    getWifiCallingCarrierActivityIntent(subId);
+        final boolean isWifiCallingEnabled = isWifiCallingEnabled();
+        final Intent activationAppIntent =
+                getWifiCallingCarrierActivityIntent(subId);
 
-            // Send this actionable wifi calling slice to toggle the setting
-            // only when there is no need for wifi calling activation with the server
-            if (activationAppIntent != null && !isWifiCallingEnabled) {
-                Log.d(TAG, "Needs Activation");
-                // Activation needed for the next action of the user
-                // Give instructions to go to settings app
-                return getNonActionableWifiCallingSlice(
-                        res.getText(R.string.wifi_calling_settings_title),
-                        res.getText(R.string.wifi_calling_settings_activation_instructions),
-                        sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
-            }
-            return getWifiCallingSlice(sliceUri, isWifiCallingEnabled, subId);
-        } catch (InterruptedException | TimeoutException | ExecutionException e) {
-            Log.e(TAG, "Unable to read the current WiFi calling status", e);
-            return null;
+        // Send this actionable wifi calling slice to toggle the setting
+        // only when there is no need for wifi calling activation with the server
+        if (activationAppIntent != null && !isWifiCallingEnabled) {
+            Log.d(TAG, "Needs Activation");
+            // Activation needed for the next action of the user
+            // Give instructions to go to settings app
+            final Resources res = getResourcesForSubId(subId);
+            return getNonActionableWifiCallingSlice(
+                    res.getText(R.string.wifi_calling_settings_title),
+                    res.getText(R.string.wifi_calling_settings_activation_instructions),
+                    sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
         }
+        return getWifiCallingSlice(sliceUri, isWifiCallingEnabled, subId);
     }
 
-    private boolean isWifiCallingEnabled(ImsManager imsManager)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        final FutureTask<Boolean> isWifiOnTask = new FutureTask<>(new Callable<Boolean>() {
-            @Override
-            public Boolean call() {
-                return imsManager.isWfcEnabledByUser();
-            }
-        });
-        final ExecutorService executor = Executors.newSingleThreadExecutor();
-        executor.execute(isWifiOnTask);
-
-        return isWifiOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
-                && imsManager.isNonTtyOrTtyOnVolteEnabled();
+    private boolean isWifiCallingEnabled() {
+        final int subId = getDefaultVoiceSubId();
+        return queryImsState(subId).isEnabledByUser()
+                && queryImsState(subId).isAllowUserControl();
     }
 
     /**
@@ -192,7 +179,7 @@
      */
     private Slice getWifiCallingSlice(Uri sliceUri, boolean isWifiCallingEnabled, int subId) {
         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
-        Resources res = getResourcesForSubId(subId);
+        final Resources res = getResourcesForSubId(subId);
 
         return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext))
@@ -228,7 +215,7 @@
     public Slice createWifiCallingPreferenceSlice(Uri sliceUri) {
         final int subId = getDefaultVoiceSubId();
 
-        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             Log.d(TAG, "Invalid Subscription Id");
             return null;
         }
@@ -254,7 +241,7 @@
         boolean isWifiCallingEnabled = false;
         int wfcMode = -1;
         try {
-            isWifiCallingEnabled = isWifiCallingEnabled(imsManager);
+            isWifiCallingEnabled = isWifiCallingEnabled();
             wfcMode = getWfcMode(imsMmTelManager);
         } catch (InterruptedException | ExecutionException | TimeoutException e) {
             Log.e(TAG, "Unable to get wifi calling preferred mode", e);
@@ -262,7 +249,7 @@
         }
         if (!isWifiCallingEnabled) {
             // wifi calling is not enabled. Ask user to enable wifi calling
-            Resources res = getResourcesForSubId(subId);
+            final Resources res = getResourcesForSubId(subId);
             return getNonActionableWifiCallingSlice(
                     res.getText(R.string.wifi_calling_mode_title),
                     res.getText(R.string.wifi_calling_turn_on),
@@ -287,7 +274,7 @@
             Uri sliceUri,
             int subId) {
         final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
-        Resources res = getResourcesForSubId(subId);
+        final Resources res = getResourcesForSubId(subId);
         // Top row shows information on current preference state
         final ListBuilder listBuilder = new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
                 .setAccentColor(Utils.getColorAccentDefaultColor(mContext));
@@ -333,7 +320,7 @@
             int preferenceTitleResId, String action, boolean checked, int subId) {
         final IconCompat icon =
                 IconCompat.createWithResource(mContext, R.drawable.radio_button_check);
-        Resources res = getResourcesForSubId(subId);
+        final Resources res = getResourcesForSubId(subId);
         return new RowBuilder()
                 .setTitle(res.getText(preferenceTitleResId))
                 .setTitleItem(SliceAction.createToggle(getBroadcastIntent(action),
@@ -348,7 +335,7 @@
      * @return summary/name of the wifi calling preference
      */
     private CharSequence getWifiCallingPreferenceSummary(int wfcMode, int subId) {
-        Resources res = getResourcesForSubId(subId);
+        final Resources res = getResourcesForSubId(subId);
         switch (wfcMode) {
             case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
                 return res.getText(
@@ -394,12 +381,12 @@
     public void handleWifiCallingChanged(Intent intent) {
         final int subId = getDefaultVoiceSubId();
 
-        if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
             final ImsManager imsManager = getImsManager(subId);
             if (imsManager.isWfcEnabledByPlatform()
                     && isWfcProvisionedOnDevice(subId)) {
-                final boolean currentValue = imsManager.isWfcEnabledByUser()
-                        && imsManager.isNonTtyOrTtyOnVolteEnabled();
+                final boolean currentValue = queryImsState(subId).isEnabledByUser()
+                        && queryImsState(subId).isAllowUserControl();
                 final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
                         currentValue);
                 final Intent activationAppIntent =
@@ -408,7 +395,8 @@
                     // If either the action is to turn off wifi calling setting
                     // or there is no activation involved - Update the setting
                     if (newValue != currentValue) {
-                        imsManager.setWfcSetting(newValue);
+                        final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
+                        imsMmTelManager.setVoWiFiSettingEnabled(newValue);
                     }
                 }
             }
@@ -431,7 +419,7 @@
         final int subId = getDefaultVoiceSubId();
         final int errorValue = -1;
 
-        if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
             final boolean isWifiCallingPrefEditable = isCarrierConfigManagerKeyEnabled(
                     CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, subId, false);
             final boolean isWifiOnlySupported = isCarrierConfigManagerKeyEnabled(
@@ -441,8 +429,8 @@
             if (isWifiCallingPrefEditable
                     && imsManager.isWfcEnabledByPlatform()
                     && isWfcProvisionedOnDevice(subId)
-                    && imsManager.isWfcEnabledByUser()
-                    && imsManager.isNonTtyOrTtyOnVolteEnabled()) {
+                    && queryImsState(subId).isEnabledByUser()
+                    && queryImsState(subId).isAllowUserControl()) {
                 // Change the preference only when wifi calling is enabled
                 // And when wifi calling preference is editable for the current carrier
                 final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
@@ -587,4 +575,9 @@
     private Resources getResourcesForSubId(int subId) {
         return SubscriptionManager.getResourcesForSubId(mContext, subId);
     }
+
+    @VisibleForTesting
+    WifiCallingQueryImsState queryImsState(int subId) {
+        return new WifiCallingQueryImsState(mContext, subId);
+    }
 }
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
index 526e9c3..0e3b887 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
@@ -21,17 +21,20 @@
 
 import com.android.ims.ImsManager;
 import com.android.settings.SettingsActivity;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 
 public class WifiCallingSuggestionActivity extends SettingsActivity {
 
     public static boolean isSuggestionComplete(Context context) {
+        final WifiCallingQueryImsState queryState =
+                new WifiCallingQueryImsState(context,
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
         if (!ImsManager.isWfcEnabledByPlatform(context) ||
                 !MobileNetworkUtils.isWfcProvisionedOnDevice(
                         SubscriptionManager.getDefaultVoiceSubscriptionId())) {
             return true;
         }
-        return ImsManager.isWfcEnabledByUser(context)
-                && ImsManager.isNonTtyOrTtyOnVolteEnabled(context);
+        return queryState.isEnabledByUser() && queryState.isAllowUserControl();
     }
 }
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
index 5e69b8a..616c92f 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
@@ -62,4 +62,9 @@
         }
         return true;
     }
+
+    @Override
+    protected int getApRowCount() {
+        return ContextualWifiSlice.getApRowCount();
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index 6b9dd3c..bb9be77 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -17,30 +17,42 @@
 package com.android.settings.wifi.slice;
 
 import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
 import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
 import android.net.Uri;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
 import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
 
+import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.CustomSliceable;
+import com.android.settingslib.wifi.AccessPoint;
 
 /**
  * {@link CustomSliceable} for Wi-Fi, used by contextual homepage.
  */
 public class ContextualWifiSlice extends WifiSlice {
 
-    private static final String TAG = "ContextualWifiSlice";
+    @VisibleForTesting
+    static final int COLLAPSED_ROW_COUNT = 0;
+
     @VisibleForTesting
     static long sActiveUiSession = -1000;
     @VisibleForTesting
-    static boolean sPreviouslyDisplayed;
+    static boolean sToggleNeeded = true;
 
     public ContextualWifiSlice(Context context) {
         super(context);
@@ -57,19 +69,77 @@
                 .getSlicesFeatureProvider().getUiSessionToken();
         if (currentUiSession != sActiveUiSession) {
             sActiveUiSession = currentUiSession;
-            sPreviouslyDisplayed = false;
+            sToggleNeeded = !hasWorkingNetwork();
+        } else if (!mWifiManager.isWifiEnabled()) {
+            sToggleNeeded = true;
         }
-        if (!sPreviouslyDisplayed && hasWorkingNetwork()) {
-            Log.d(TAG, "Wifi is connected, no point showing any suggestion.");
-            return null;
-        }
-        // Set sPreviouslyDisplayed to true - we will show *something* on the screen. So we should
-        // keep showing this card to keep UI stable, even if wifi connects to a network later.
-        sPreviouslyDisplayed = true;
-
         return super.getSlice();
     }
 
+    static int getApRowCount() {
+        return sToggleNeeded ? DEFAULT_EXPANDED_ROW_COUNT : COLLAPSED_ROW_COUNT;
+    }
+
+    @Override
+    protected boolean isToggleNeeded() {
+        return sToggleNeeded;
+    }
+
+    @Override
+    protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
+        final ListBuilder.RowBuilder builder = super.getHeaderRow(accessPoint);
+        if (!sToggleNeeded) {
+            builder.setTitleItem(getLevelIcon(accessPoint), ListBuilder.ICON_IMAGE)
+                    .setSubtitle(getSubtitle(accessPoint));
+        }
+        return builder;
+    }
+
+    private IconCompat getLevelIcon(AccessPoint accessPoint) {
+        if (accessPoint != null) {
+            return getAccessPointLevelIcon(accessPoint);
+        }
+
+        final Drawable drawable = mContext.getDrawable(
+                com.android.settingslib.Utils.getWifiIconResource(0));
+        final int color = Utils.getColorAttrDefaultColor(mContext,
+                android.R.attr.colorControlNormal);
+        drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
+        return Utils.createIconWithDrawable(drawable);
+    }
+
+    private CharSequence getSubtitle(AccessPoint accessPoint) {
+        if (isCaptivePortal()) {
+            final int id = mContext.getResources()
+                    .getIdentifier("network_available_sign_in", "string", "android");
+            return mContext.getText(id);
+        }
+
+        if (accessPoint == null) {
+            return mContext.getText(R.string.disconnected);
+        }
+
+        final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+        if (networkInfo == null) {
+            return mContext.getText(R.string.disconnected);
+        }
+
+        final State state = networkInfo.getState();
+        DetailedState detailedState;
+        if (state == State.CONNECTING) {
+            detailedState = DetailedState.CONNECTING;
+        } else if (state == State.CONNECTED) {
+            detailedState = DetailedState.CONNECTED;
+        } else {
+            detailedState = networkInfo.getDetailedState();
+        }
+
+        final String[] formats = mContext.getResources().getStringArray(
+                R.array.wifi_status_with_ssid);
+        final int index = detailedState.ordinal();
+        return String.format(formats[index], accessPoint.getTitle());
+    }
+
     private boolean hasWorkingNetwork() {
         return !TextUtils.equals(getActiveSSID(), WifiManager.UNKNOWN_SSID) && hasInternetAccess();
     }
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index a5bd74d..be1e005 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -111,10 +111,11 @@
         // AccessPoints are sorted by the WifiTracker
         final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
         final List<AccessPoint> resultList = new ArrayList<>();
+        final int apRowCount = getApRowCount();
         for (AccessPoint ap : accessPoints) {
             if (ap.isReachable()) {
                 resultList.add(clone(ap));
-                if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) {
+                if (resultList.size() >= apRowCount) {
                     break;
                 }
             }
@@ -122,6 +123,10 @@
         updateResults(resultList);
     }
 
+    protected int getApRowCount() {
+        return DEFAULT_EXPANDED_ROW_COUNT;
+    }
+
     private AccessPoint clone(AccessPoint accessPoint) {
         final Bundle savedState = new Bundle();
         accessPoint.saveWifiState(savedState);
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 97ac9c5..8e2d7a6 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -95,7 +95,7 @@
         mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);
 
         final boolean isWifiEnabled = isWifiEnabled();
-        ListBuilder listBuilder = getHeaderRow(isWifiEnabled);
+        ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */);
         if (!isWifiEnabled) {
             WifiScanWorker.clearClickedWifi();
             return listBuilder.build();
@@ -107,17 +107,12 @@
         final boolean isFirstApActive = apCount > 0 && apList.get(0).isActive();
         handleNetworkCallback(worker, isFirstApActive);
 
-        // Need a loading text when results are not ready or out of date.
-        boolean needLoadingRow = true;
-        // Skip checking the existence of the first access point if it's active
-        int index = isFirstApActive ? 1 : 0;
-        // This loop checks the existence of reachable APs to determine the validity of the current
-        // AP list.
-        for (; index < apCount; index++) {
-            if (apList.get(index).isReachable()) {
-                needLoadingRow = false;
-                break;
+        if (!isToggleNeeded()) {
+            if (isFirstApActive) {
+                // refresh header subtext
+                listBuilder = getListBuilder(true /* isWifiEnabled */, apList.get(0));
             }
+            return listBuilder.build();
         }
 
         // Add AP rows
@@ -125,9 +120,8 @@
         for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) {
             if (i < apCount) {
                 listBuilder.addRow(getAccessPointRow(apList.get(i)));
-            } else if (needLoadingRow) {
+            } else if (i == apCount) {
                 listBuilder.addRow(getLoadingRow(placeholder));
-                needLoadingRow = false;
             } else {
                 listBuilder.addRow(new ListBuilder.RowBuilder()
                         .setTitle(placeholder)
@@ -148,24 +142,34 @@
         }
     }
 
-    private ListBuilder getHeaderRow(boolean isWifiEnabled) {
+    protected boolean isToggleNeeded() {
+        return true;
+    }
+
+    protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
         final IconCompat icon = IconCompat.createWithResource(mContext,
                 R.drawable.ic_settings_wireless);
         final String title = mContext.getString(R.string.wifi_settings);
-        final PendingIntent toggleAction = getBroadcastIntent(mContext);
         final PendingIntent primaryAction = getPrimaryAction();
         final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
                 ListBuilder.ICON_IMAGE, title);
-        final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
-                null /* actionTitle */, isWifiEnabled);
 
-        return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+        return new ListBuilder.RowBuilder()
+                .setTitle(title)
+                .setPrimaryAction(primarySliceAction);
+    }
+
+    private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) {
+        final ListBuilder builder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
                 .setAccentColor(COLOR_NOT_TINTED)
                 .setKeywords(getKeywords())
-                .addRow(new ListBuilder.RowBuilder()
-                        .setTitle(title)
-                        .addEndItem(toggleSliceAction)
-                        .setPrimaryAction(primarySliceAction));
+                .addRow(getHeaderRow(accessPoint));
+        if (isToggleNeeded()) {
+            final PendingIntent toggleAction = getBroadcastIntent(mContext);
+            builder.addAction(SliceAction.createToggle(toggleAction, null /* actionTitle */,
+                    isWifiEnabled));
+        }
+        return builder;
     }
 
     private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
@@ -200,7 +204,7 @@
         return TextUtils.isEmpty(summary) ? mContext.getText(R.string.disconnected) : summary;
     }
 
-    private IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) {
+    protected IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) {
         final Drawable d = mContext.getDrawable(
                 com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel()));
 
@@ -250,24 +254,16 @@
         accessPoint.saveWifiState(extras);
 
         if (accessPoint.isActive()) {
-            Intent intent;
+            final SubSettingLauncher launcher = new SubSettingLauncher(mContext)
+                    .setTitleRes(R.string.pref_title_network_details)
+                    .setArguments(extras)
+                    .setSourceMetricsCategory(SettingsEnums.WIFI);
             if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
-                intent = new SubSettingLauncher(mContext)
-                        .setTitleRes(R.string.pref_title_network_details)
-                        .setDestination(WifiNetworkDetailsFragment2.class.getName())
-                        .setArguments(extras)
-                        .setSourceMetricsCategory(SettingsEnums.WIFI)
-                        .toIntent();
-                return getActivityAction(requestCode, intent, icon, title);
+                launcher.setDestination(WifiNetworkDetailsFragment2.class.getName());
             } else {
-                intent = new SubSettingLauncher(mContext)
-                        .setTitleRes(R.string.pref_title_network_details)
-                        .setDestination(WifiNetworkDetailsFragment.class.getName())
-                        .setArguments(extras)
-                        .setSourceMetricsCategory(SettingsEnums.WIFI)
-                        .toIntent();
-                return getActivityAction(requestCode, intent, icon, title);
+                launcher.setDestination(WifiNetworkDetailsFragment.class.getName());
             }
+            return getActivityAction(requestCode, launcher.toIntent(), icon, title);
         } else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
             final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
                     .putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
@@ -307,7 +303,7 @@
                 .setSubtitle(title);
     }
 
-    private boolean isCaptivePortal() {
+    protected boolean isCaptivePortal() {
         final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
                 mWifiManager.getCurrentNetwork());
         return WifiUtils.canSignIntoNetwork(nc);
diff --git a/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java
new file mode 100644
index 0000000..9316735
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.applications;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+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.pm.PackageManager;
+import android.util.ArraySet;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppOpenSupportedLinksPreferenceControllerTest {
+    private static final String TEST_KEY = "test_key";
+    private static final String TEST_DOMAIN_LINK = "aaa.bbb.ccc";
+    private static final String TEST_PACKAGE = "ssl.test.package.com";
+
+    @Mock
+    private PackageManager mPackageManager;
+
+    private Context mContext;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mScreen;
+    private PreferenceCategory mCategory;
+    private AppOpenSupportedLinksPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        mPreferenceManager = new PreferenceManager(mContext);
+        mScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+        mCategory = spy(new PreferenceCategory(mContext));
+        mController = spy(new AppOpenSupportedLinksPreferenceController(mContext, TEST_KEY));
+        mController.setInit(TEST_PACKAGE);
+    }
+
+    @Test
+    public void displayPreference_statusAlways_allowOpenChecked() {
+        init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mController.mAllowOpening.isChecked()).isTrue();
+        assertThat(mController.mAskEveryTime.isChecked()).isFalse();
+        assertThat(mController.mNotAllowed.isChecked()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_statusAsk_askEveryTimeChecked() {
+        init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mController.mAllowOpening.isChecked()).isFalse();
+        assertThat(mController.mAskEveryTime.isChecked()).isTrue();
+        assertThat(mController.mNotAllowed.isChecked()).isFalse();
+    }
+
+    @Test
+    public void displayPreference_statusNever_notAllowedChecked() {
+        init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER);
+
+        mController.displayPreference(mScreen);
+
+        assertThat(mController.mAllowOpening.isChecked()).isFalse();
+        assertThat(mController.mAskEveryTime.isChecked()).isFalse();
+        assertThat(mController.mNotAllowed.isChecked()).isTrue();
+    }
+
+    @Test
+    @Config(shadows = ShadowUtils.class)
+    public void getEntriesNo_oneHandledDomains_returnOne() {
+        initHandledDomains();
+
+        assertThat(mController.getEntriesNo()).isEqualTo(1);
+    }
+
+    private void init(int status) {
+        doReturn(mCategory).when(mScreen).findPreference(any(CharSequence.class));
+        doReturn(true).when(mCategory).addPreference(any(Preference.class));
+        when(mPackageManager.getIntentVerificationStatusAsUser(anyString(), anyInt())).thenReturn(
+                status);
+    }
+
+    private void initHandledDomains() {
+        final ArraySet<String> domainLinks = new ArraySet<>();
+        domainLinks.add(TEST_DOMAIN_LINK);
+        ShadowUtils.setHandledDomains(domainLinks);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java b/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java
new file mode 100644
index 0000000..f9d4ca8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.widget.FooterPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class OpenSupportedLinksTest {
+    private static final String TEST_FOOTER_TITLE = "FooterTitle";
+    private static final String TEST_DOMAIN_LINK = "aaa.bbb.ccc";
+
+    private Context mContext;
+    private TestFragment mSettings;
+    private FooterPreference mFooter;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        mSettings = spy(new TestFragment(mContext));
+        mFooter = new FooterPreference.Builder(mContext).setTitle(TEST_FOOTER_TITLE).build();
+    }
+
+    @After
+    public void tearDown() {
+        ShadowUtils.reset();
+    }
+
+    @Test
+    public void addLinksToFooter_noHandledDomains_returnDefaultFooterTitle() {
+        mSettings.addLinksToFooter(mFooter);
+
+        assertThat(mFooter.getTitle()).isEqualTo(TEST_FOOTER_TITLE);
+    }
+
+    @Test
+    public void addLinksToFooter_oneHandledDomains_returnDomainsFooterTitle() {
+        final ArraySet<String> domainLinks = new ArraySet<>();
+        domainLinks.add(TEST_DOMAIN_LINK);
+        ShadowUtils.setHandledDomains(domainLinks);
+
+        mSettings.addLinksToFooter(mFooter);
+
+        assertThat(mFooter.getTitle().toString()).contains(TEST_DOMAIN_LINK);
+    }
+
+    public static class TestFragment extends OpenSupportedLinks {
+        private final Context mContext;
+
+        public TestFragment(Context context) {
+            mContext = context;
+            mPackageInfo = new PackageInfo();
+            mPackageInfo.packageName = "ssl.test.package.com";
+        }
+
+        @Override
+        protected PackageManager getPackageManager() {
+            return mContext.getPackageManager();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
index d0cb97a..e558d62 100644
--- a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
+++ b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
@@ -105,6 +105,7 @@
         mDashboard = spy(new PlatformCompatDashboard());
         mDashboard.mSelectedApp = APP_NAME;
         doReturn(mApplicationInfo).when(mDashboard).getApplicationInfo();
+        doReturn(mContext).when(mDashboard).getContext();
         doReturn(mPlatformCompat).when(mDashboard).getPlatformCompat();
         doReturn(mPreferenceScreen).when(mDashboard).getPreferenceScreen();
         doReturn(mPreferenceManager).when(mDashboard).getPreferenceManager();
@@ -130,7 +131,8 @@
 
         Preference appPreference = mDashboard.createAppPreference(mApplicationInfo);
 
-        assertThat(appPreference.getSummary()).isEqualTo(APP_NAME + " SDK 1");
+        assertThat(appPreference.getSummary()).isEqualTo(mContext.getResources().getString(
+                R.string.platform_compat_selected_app_summary, APP_NAME, 1));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
index cd20ea2..25117be 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
@@ -32,7 +32,10 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Locale;
+
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -60,9 +63,12 @@
     private Button mTurnOnButton;
     @Mock
     private PowerManager mPM;
+    @Mock
+    private TimeFormatter mFormat;
 
-    private Configuration configNightYes = new Configuration();
-    private Configuration configNightNo = new Configuration();;
+    private Configuration mConfigNightYes = new Configuration();
+    private Configuration mConfigNightNo = new Configuration();
+    private Locale mLocal = new Locale("ENG");
 
     @Before
     public void setUp() {
@@ -77,6 +83,7 @@
         when(mPreference.findViewById(
                 eq(R.id.dark_ui_turn_off_button))).thenReturn(mTurnOffButton);
         when(mService.setNightModeActivated(anyBoolean())).thenReturn(true);
+        when(mFormat.of(any())).thenReturn("10:00 AM");
         when(mContext.getString(
                 R.string.dark_ui_activation_off_auto)).thenReturn("off_auto");
         when(mContext.getString(
@@ -93,16 +100,22 @@
                 R.string.dark_ui_summary_off_auto_mode_never)).thenReturn("summary_off_manual");
         when(mContext.getString(
                 R.string.dark_ui_summary_on_auto_mode_never)).thenReturn("summary_on_manual");
-        mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey);
+        when(mContext.getString(
+                R.string.dark_ui_summary_on_auto_mode_custom)).thenReturn("summary_on_custom");
+        when(mContext.getString(
+                R.string.dark_ui_summary_off_auto_mode_custom)).thenReturn("summary_off_custom");
+        mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey, mFormat);
         mController.displayPreference(mScreen);
-        configNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
-        configNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
+        mConfigNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
+        mConfigNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
+        mConfigNightNo.locale = mLocal;
+        mConfigNightYes.locale = mLocal;
     }
 
     @Test
     public void nightMode_toggleButton_offManual() {
         when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
-        when(res.getConfiguration()).thenReturn(configNightYes);
+        when(res.getConfiguration()).thenReturn(mConfigNightYes);
 
         mController.updateState(mPreference);
 
@@ -112,11 +125,37 @@
                 R.string.dark_ui_activation_off_manual)));
     }
 
+    @Test
+    public void nightMode_toggleButton_offCustom() {
+        when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+        when(res.getConfiguration()).thenReturn(mConfigNightYes);
+
+        mController.updateState(mPreference);
+
+        verify(mTurnOnButton).setVisibility(eq(View.GONE));
+        verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
+        verify(mTurnOffButton).setText(eq(mContext.getString(
+                R.string.dark_ui_activation_off_custom)));
+    }
+
+    @Test
+    public void nightMode_toggleButton_onCustom() {
+        when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+        when(res.getConfiguration()).thenReturn(mConfigNightYes);
+
+        mController.updateState(mPreference);
+
+        verify(mTurnOnButton).setVisibility(eq(View.GONE));
+        verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
+        verify(mTurnOffButton).setText(eq(mContext.getString(
+                R.string.dark_ui_activation_on_custom)));
+    }
+
 
     @Test
     public void nightMode_toggleButton_onAutoWhenModeIsYes() {
         when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
-        when(res.getConfiguration()).thenReturn(configNightNo);
+        when(res.getConfiguration()).thenReturn(mConfigNightNo);
 
         mController.updateState(mPreference);
 
@@ -129,7 +168,7 @@
     @Test
     public void nightMode_toggleButton_onAutoWhenModeIsAuto() {
         when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
-        when(res.getConfiguration()).thenReturn(configNightNo);
+        when(res.getConfiguration()).thenReturn(mConfigNightNo);
 
         mController.updateState(mPreference);
 
@@ -142,7 +181,7 @@
     @Test
     public void nightModeSummary_buttonText_onManual() {
         when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO);
-        when(res.getConfiguration()).thenReturn(configNightYes);
+        when(res.getConfiguration()).thenReturn(mConfigNightYes);
 
         assertEquals(mController.getSummary(), mContext.getString(
                 R.string.dark_ui_summary_on_auto_mode_never));
@@ -151,7 +190,7 @@
     @Test
     public void nightModeSummary_buttonText_offAuto() {
         when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
-        when(res.getConfiguration()).thenReturn(configNightNo);
+        when(res.getConfiguration()).thenReturn(mConfigNightNo);
 
         assertEquals(mController.getSummary(), mContext.getString(
                 R.string.dark_ui_summary_off_auto_mode_auto));
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java
new file mode 100644
index 0000000..107b79f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import androidx.preference.Preference;
+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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkModeCustomPreferenceControllerTest {
+    private DarkModeCustomPreferenceController mController;
+    @Mock
+    private UiModeManager mService;
+    @Mock
+    private Context mContext;
+    @Mock
+    private Preference mPreference;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private ContentResolver mCR;
+    @Mock
+    private TimeFormatter mFormat;
+    @Mock
+    private DarkModeSettingsFragment mFragment;
+    private Configuration mConfig = new Configuration();
+
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getConfiguration()).thenReturn(mConfig);
+        when(mContext.getContentResolver()).thenReturn(mCR);
+        mService = mock(UiModeManager.class);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
+        mController = new DarkModeCustomPreferenceController(mContext, "key", mFragment, mFormat);
+        when(mFormat.is24HourFormat()).thenReturn(false);
+        when(mFormat.of(any())).thenReturn("10:00 AM");
+    }
+
+    @Test
+    public void nightMode_customOff_hidePreference() {
+        when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+        mController.refreshSummary(mPreference);
+        verify(mPreference).setVisible(eq(false));
+    }
+
+    @Test
+    public void nightMode_customOff_showPreference() {
+        when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+        mController.refreshSummary(mPreference);
+        verify(mPreference).setVisible(eq(true));
+    }
+
+    @Test
+    public void nightMode_customOff_setSummaryNotNull() {
+        when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+        mController.refreshSummary(mPreference);
+        verify(mPreference).setSummary(eq(mFormat.of(null)));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
index b1c6738..8afda73 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
@@ -65,6 +65,7 @@
         when(mContext.getSystemService(PowerManager.class)).thenReturn(mPM);
         when(mContext.getString(R.string.dark_ui_auto_mode_never)).thenReturn("never");
         when(mContext.getString(R.string.dark_ui_auto_mode_auto)).thenReturn("auto");
+        when(mContext.getString(R.string.dark_ui_auto_mode_custom)).thenReturn("custom");
         mPreference = spy(new DropDownPreference(mContext));
         mPreference.setEntryValues(new CharSequence[]{
                 mContext.getString(R.string.dark_ui_auto_mode_never),
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 af37797..be3c685 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
@@ -17,9 +17,9 @@
 package com.android.settings.homepage.contextualcards;
 
 import static com.android.settings.homepage.contextualcards.ContextualCardManager.KEY_CONTEXTUAL_CARDS;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
 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;
+import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_STICKY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -110,8 +110,8 @@
         mManager = new ContextualCardManager(mContext, mLifecycle, outState);
 
         final List<String> actualCards = mManager.mSavedCards.stream().collect(Collectors.toList());
-        final List<String> expectedCards = Arrays.asList("test_wifi", "test_flashlight",
-                "test_connected", "test_gesture", "test_battery");
+        final List<String> expectedCards = Arrays.asList("test_low_storage", "test_flashlight",
+                "test_dark_theme", "test_gesture", "test_battery");
         assertThat(actualCards).containsExactlyElementsIn(expectedCards);
     }
 
@@ -348,8 +348,9 @@
         final ConditionContextualCardController conditionController =
                 pool.getController(mContext,
                         ContextualCard.CardType.CONDITIONAL);
-        final OnStart controller = spy((OnStart)conditionController);
-        doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+        final OnStart controller = spy((OnStart) conditionController);
+        doReturn(controller).when(pool).getController(mContext,
+                ContextualCard.CardType.CONDITIONAL);
 
         manager.onWindowFocusChanged(true /* hasWindowFocus */);
 
@@ -366,8 +367,9 @@
         final ConditionContextualCardController conditionController =
                 pool.getController(mContext,
                         ContextualCard.CardType.CONDITIONAL);
-        final OnStart controller = spy((OnStart)conditionController);
-        doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+        final OnStart controller = spy((OnStart) conditionController);
+        doReturn(controller).when(pool).getController(mContext,
+                ContextualCard.CardType.CONDITIONAL);
 
         manager.onWindowFocusChanged(true /* hasWindowFocus */);
 
@@ -385,7 +387,8 @@
                 pool.getController(mContext,
                         ContextualCard.CardType.CONDITIONAL);
         final OnStop controller = spy((OnStop) conditionController);
-        doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+        doReturn(controller).when(pool).getController(mContext,
+                ContextualCard.CardType.CONDITIONAL);
 
         manager.onWindowFocusChanged(false /* hasWindowFocus */);
 
@@ -401,7 +404,8 @@
                 pool.getController(mContext,
                         ContextualCard.CardType.CONDITIONAL);
         final OnStop controller = spy((OnStop) conditionController);
-        doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+        doReturn(controller).when(pool).getController(mContext,
+                ContextualCard.CardType.CONDITIONAL);
 
         manager.onWindowFocusChanged(false /* hasWindowFocus */);
 
@@ -542,35 +546,65 @@
     }
 
     @Test
-    public void getCardsWithViewType_onlyDeferredSetupCard_shouldHaveDeferredSetupCard() {
-        final List<ContextualCard> oneDeferredSetupCards = getDeferredSetupCardList();
-
-        final List<ContextualCard> result = mManager.getCardsWithViewType(oneDeferredSetupCards);
-
-        assertThat(result).hasSize(1);
-        assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
-    }
-
-    @Test
-    public void getCardsWithViewType_hasDeferredSetupCard_shouldHaveDeferredSetupCard() {
+    public void getCardsWithViewType_hasWifiSlice_shouldHaveOneStickyCard() {
+        final List<ContextualCard> cards = new ArrayList<>();
+        cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString()));
+        cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
         final List<Integer> categories = Arrays.asList(
-                ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE,
                 ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
-                ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
-                ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
                 ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
         );
-        final List<ContextualCard> cards = buildCategoriedCards(getContextualCardList(),
-                categories);
+        final List<ContextualCard> cardListWithWifi = buildCategoriedCards(cards, categories);
 
-        final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
+        final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithWifi);
 
-        assertThat(result).hasSize(5);
-        assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
+        assertThat(result).hasSize(cards.size());
+        assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_STICKY);
+        assertThat(result.get(1).getViewType()).isEqualTo(VIEW_TYPE_FULL_WIDTH);
     }
 
     @Test
-    public void getCardsWithViewType_noDeferredSetupCard_shouldNotHaveDeferredSetupCard() {
+    public void getCardsWithViewType_hasBluetoothDeviceSlice_shouldHaveOneStickyCard() {
+        final List<ContextualCard> cards = new ArrayList<>();
+        cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()));
+        cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
+        final List<Integer> categories = Arrays.asList(
+                ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+                ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
+        );
+        final List<ContextualCard> cardListWithBT = buildCategoriedCards(cards, categories);
+
+        final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithBT);
+
+        assertThat(result).hasSize(cards.size());
+        assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_STICKY);
+        assertThat(result.get(1).getViewType()).isEqualTo(VIEW_TYPE_FULL_WIDTH);
+    }
+
+    @Test
+    public void getCardsWithViewType_hasWifiAndBtDeviceSlice_shouldHaveTwoStickyCards() {
+        final List<ContextualCard> cards = new ArrayList<>();
+        cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString()));
+        cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()));
+        cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
+        final List<Integer> categories = Arrays.asList(
+                ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+                ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+                ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
+        );
+        final List<ContextualCard> cardListWithWifiBT = buildCategoriedCards(cards, categories);
+
+        final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithWifiBT);
+
+        assertThat(result).hasSize(cards.size());
+        assertThat(result.stream()
+                .filter(card -> card.getViewType() == VIEW_TYPE_STICKY)
+                .count())
+                .isEqualTo(2);
+    }
+
+    @Test
+    public void getCardsWithViewType_noWifiOrBtDeviceSlice_shouldNotHaveStickyCard() {
         final List<Integer> categories = Arrays.asList(
                 ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
                 ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -578,16 +612,16 @@
                 ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
                 ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
         );
-        final List<ContextualCard> cards = buildCategoriedCards(
-                getContextualCardList(), categories);
+        final List<ContextualCard> cardListWithoutWifiBT =
+                buildCategoriedCards(getContextualCardList(), categories);
 
-        final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
+        final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithoutWifiBT);
 
-        assertThat(result).hasSize(5);
-        for (int i = 0; i < result.size(); i++) {
-            assertThat(result.get(i).getViewType()).isNotEqualTo(
-                    ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE);
-        }
+        assertThat(result).hasSize(cardListWithoutWifiBT.size());
+        assertThat(result.stream()
+                .filter(card -> card.getViewType() == VIEW_TYPE_STICKY)
+                .count())
+                .isEqualTo(0);
     }
 
     @Test
@@ -622,9 +656,9 @@
     private List<ContextualCard> getContextualCardList() {
         final List<ContextualCard> cards = new ArrayList<>();
         cards.add(new ContextualCard.Builder()
-                .setName("test_wifi")
+                .setName("test_low_storage")
                 .setCardType(ContextualCard.CardType.SLICE)
-                .setSliceUri(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI)
+                .setSliceUri(CustomSliceRegistry.LOW_STORAGE_SLICE_URI)
                 .setViewType(VIEW_TYPE_FULL_WIDTH)
                 .build());
         cards.add(new ContextualCard.Builder()
@@ -635,9 +669,9 @@
                 .setViewType(VIEW_TYPE_FULL_WIDTH)
                 .build());
         cards.add(new ContextualCard.Builder()
-                .setName("test_connected")
+                .setName("test_dark_theme")
                 .setCardType(ContextualCard.CardType.SLICE)
-                .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI)
+                .setSliceUri(CustomSliceRegistry.DARK_THEME_SLICE_URI)
                 .setViewType(VIEW_TYPE_FULL_WIDTH)
                 .build());
         cards.add(new ContextualCard.Builder()
@@ -655,16 +689,4 @@
                 .build());
         return cards;
     }
-
-    private List<ContextualCard> getDeferredSetupCardList() {
-        final List<ContextualCard> cards = new ArrayList<>();
-        cards.add(new ContextualCard.Builder()
-                .setName("deferred_setup")
-                .setCardType(ContextualCard.CardType.SLICE)
-                .setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
-                .setSliceUri(new Uri.Builder().appendPath("test_deferred_setup_path").build())
-                .setViewType(VIEW_TYPE_FULL_WIDTH)
-                .build());
-        return cards;
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
index 26d6498..987aa8a 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
@@ -16,9 +16,6 @@
 
 package com.android.settings.homepage.contextualcards;
 
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Activity;
@@ -39,7 +36,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.android.controller.ActivityController;
@@ -156,7 +152,7 @@
                 .setName("test_name")
                 .setCardType(ContextualCard.CardType.SLICE)
                 .setSliceUri(TEST_SLICE_URI)
-                .setViewType(VIEW_TYPE_DEFERRED_SETUP)
+                .setViewType(SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH)
                 .build());
         cards.add(new ContextualCard.Builder()
                 .setName("test_name_1")
@@ -172,7 +168,7 @@
                 .setName(TEST_SLICE_NAME)
                 .setCardType(ContextualCard.CardType.SLICE)
                 .setSliceUri(sliceUri)
-                .setViewType(VIEW_TYPE_FULL_WIDTH)
+                .setViewType(SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH)
                 .build();
     }
 
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
index 4a23c33..0eda973 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 
@@ -26,9 +27,9 @@
 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.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.content.Intent;
 
@@ -37,11 +38,13 @@
 import androidx.slice.SliceItem;
 import androidx.slice.SliceMetadata;
 import androidx.slice.SliceProvider;
+import androidx.slice.core.SliceAction;
 import androidx.slice.core.SliceQuery;
 import androidx.slice.widget.SliceLiveData;
 
 import com.android.settings.R;
 import com.android.settings.testutils.SliceTester;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
 import org.junit.After;
@@ -52,6 +55,10 @@
 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.shadow.api.Shadow;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -101,18 +108,105 @@
     }
 
     @Test
-    public void getSlice_hasBluetoothDevices_shouldHaveBluetoothDevicesTitle() {
+    @Config(shadows = ShadowNoBluetoothAdapter.class)
+    public void getSlice_noBluetoothHardware_shouldReturnNull() {
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        assertThat(slice).isNull();
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_bluetoothOff_shouldHaveToggle() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_OFF);
+
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        assertTitleAndIcon(metadata);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).hasSize(1);
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_bluetoothOn_shouldNotHaveToggle() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
+
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        assertTitleAndIcon(metadata);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).isEmpty();
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_bluetoothTurningOff_shouldHaveToggle() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
+        final Intent intent = new Intent().putExtra(EXTRA_TOGGLE_STATE, false);
+
+        mBluetoothDevicesSlice.onNotifyChange(intent);
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).hasSize(1);
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_bluetoothTurningOn_shouldHaveToggle() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_OFF);
+        final Intent intent = new Intent().putExtra(EXTRA_TOGGLE_STATE, true);
+
+        mBluetoothDevicesSlice.onNotifyChange(intent);
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).isEmpty();
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_noBluetoothDevice_shouldHavePairNewDeviceRow() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
+        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
+
+        final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+        final List<SliceItem> sliceItems = slice.getItems();
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, mContext.getString(
+                R.string.bluetooth_pairing_pref_title));
+    }
+
+    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
+    public void getSlice_hasBluetoothDevices_shouldNotHavePairNewDeviceRow() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
         mockBluetoothDeviceList(1);
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
 
         final Slice slice = mBluetoothDevicesSlice.getSlice();
 
-        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
-        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_devices));
+        final List<SliceItem> sliceItems = slice.getItems();
+        SliceTester.assertNoSliceItemContainsTitle(sliceItems, mContext.getString(
+                R.string.bluetooth_pairing_pref_title));
     }
 
     @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
     public void getSlice_hasBluetoothDevices_shouldMatchBluetoothMockTitle() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
         mockBluetoothDeviceList(1);
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
 
@@ -123,7 +217,10 @@
     }
 
     @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
     public void getSlice_hasMediaBluetoothDevice_shouldBuildMediaBluetoothAction() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
         mockBluetoothDeviceList(1 /* deviceCount */);
         doReturn(true).when(mBluetoothDeviceList.get(0)).isConnectedA2dpDevice();
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -134,7 +231,10 @@
     }
 
     @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
     public void getSlice_noMediaBluetoothDevice_shouldNotBuildMediaBluetoothAction() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
         mockBluetoothDeviceList(1 /* deviceCount */);
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
 
@@ -144,18 +244,10 @@
     }
 
     @Test
-    public void getSlice_noBluetoothDevices_shouldHaveNoBluetoothDevicesTitle() {
-        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
-
-        final Slice slice = mBluetoothDevicesSlice.getSlice();
-
-        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
-        assertThat(metadata.getTitle()).isEqualTo(
-                mContext.getString(R.string.no_bluetooth_devices));
-    }
-
-    @Test
+    @Config(shadows = ShadowBluetoothAdapter.class)
     public void getSlice_exceedDefaultRowCount_shouldOnlyShowDefaultRows() {
+        final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        adapter.setState(BluetoothAdapter.STATE_ON);
         mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
 
@@ -167,20 +259,6 @@
     }
 
     @Test
-    public void getSlice_exceedDefaultRowCount_shouldContainDefaultCountInSubTitle() {
-        mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
-        doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
-
-        final Slice slice = mBluetoothDevicesSlice.getSlice();
-
-        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
-        assertThat(metadata.getSubtitle()).isEqualTo(
-                mContext.getResources().getQuantityString(R.plurals.show_bluetooth_devices,
-                        BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT,
-                        BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT));
-    }
-
-    @Test
     public void onNotifyChange_mediaDevice_shouldActivateDevice() {
         mockBluetoothDeviceList(1);
         doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -201,4 +279,22 @@
             mBluetoothDeviceList.add(mCachedBluetoothDevice);
         }
     }
+
+    private void assertTitleAndIcon(SliceMetadata metadata) {
+        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(
+                R.string.bluetooth_settings_title));
+
+        final SliceAction primaryAction = metadata.getPrimaryAction();
+        final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
+                com.android.internal.R.drawable.ic_settings_bluetooth);
+        assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+    }
+
+    @Implements(BluetoothAdapter.class)
+    public static class ShadowNoBluetoothAdapter extends ShadowBluetoothAdapter {
+        @Implementation
+        protected static BluetoothAdapter getDefaultAdapter() {
+            return null;
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
index e2eb9bd..4da5c09 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
@@ -20,7 +20,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
index fb04dac..70761cf 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
@@ -16,13 +16,11 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
 import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
 
 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.verify;
 
 import android.app.Activity;
@@ -163,15 +161,6 @@
     }
 
     @Test
-    public void bindView_deferredSetupCard_shouldNotCrash() {
-        final RecyclerView.ViewHolder viewHolder = getDeferredSetupViewHolder();
-        final ContextualCard card = buildContextualCard(TEST_SLICE_URI);
-        mRenderer.mSliceLiveDataMap.put(TEST_SLICE_URI, mSliceLiveData);
-
-        mRenderer.bindView(viewHolder, card);
-    }
-
-    @Test
     public void viewClick_keepCard_shouldShowSlice() {
         final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
         final View sliceView = viewHolder.itemView.findViewById(R.id.slice_view);
@@ -257,18 +246,6 @@
         return mRenderer.createViewHolder(view, VIEW_TYPE_FULL_WIDTH);
     }
 
-    private RecyclerView.ViewHolder getDeferredSetupViewHolder() {
-        final RecyclerView recyclerView = new RecyclerView(mActivity);
-        recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
-        final View view = LayoutInflater.from(mActivity)
-                .inflate(VIEW_TYPE_DEFERRED_SETUP, recyclerView, false);
-        final RecyclerView.ViewHolder viewHolder = spy(
-                new SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder(view));
-        doReturn(VIEW_TYPE_DEFERRED_SETUP).when(viewHolder).getItemViewType();
-
-        return viewHolder;
-    }
-
     private ContextualCard buildContextualCard(Uri sliceUri) {
         return new ContextualCard.Builder()
                 .setName("test_name")
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java
deleted file mode 100644
index 740a45b..0000000
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.slices;
-
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-import static com.android.settings.homepage.contextualcards.slices.SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.Slice;
-import androidx.slice.SliceProvider;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.builders.SliceAction;
-import androidx.slice.widget.SliceLiveData;
-
-import com.android.settings.R;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settings.intelligence.ContextualCardProto;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class SliceDeferredSetupCardRendererHelperTest {
-    private static final Uri TEST_SLICE_URI = Uri.parse("content://test/test");
-    private static final CharSequence TITLE = "test_title";
-    private static final CharSequence SUMMARY = "test_summary";
-
-    private Activity mActivity;
-    private SliceDeferredSetupCardRendererHelper mHelper;
-
-    @Before
-    public void setUp() {
-        // Set-up specs for SliceMetadata.
-        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
-        mActivity = Robolectric.buildActivity(Activity.class).create().get();
-        mActivity.setTheme(R.style.Theme_Settings_Home);
-        mHelper = new SliceDeferredSetupCardRendererHelper(mActivity);
-    }
-
-    @Test
-    public void createViewHolder_shouldAlwaysReturnCustomViewHolder() {
-        final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
-        assertThat(viewHolder).isInstanceOf(
-                DeferredSetupCardViewHolder.class);
-    }
-
-    @Test
-    public void bindView_shouldSetTitle() {
-        final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
-        mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
-
-        assertThat(((DeferredSetupCardViewHolder) viewHolder).title.getText()).isEqualTo(TITLE);
-    }
-
-    @Test
-    public void bindView_shouldSetSummary() {
-        final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
-        mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
-
-        assertThat(((DeferredSetupCardViewHolder) viewHolder).summary.getText()).isEqualTo(SUMMARY);
-    }
-
-    private RecyclerView.ViewHolder getDeferredSetupCardViewHolder() {
-        final RecyclerView recyclerView = new RecyclerView(mActivity);
-        recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
-        final View view = LayoutInflater.from(mActivity).inflate(VIEW_TYPE_DEFERRED_SETUP,
-                recyclerView, false);
-
-        return mHelper.createViewHolder(view);
-    }
-
-    private ContextualCard buildContextualCard() {
-        return new ContextualCard.Builder()
-                .setName("test_name")
-                .setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
-                .setCardType(ContextualCard.CardType.SLICE)
-                .setSliceUri(TEST_SLICE_URI)
-                .setViewType(VIEW_TYPE_DEFERRED_SETUP)
-                .build();
-    }
-
-    private Slice buildSlice() {
-        final IconCompat icon = IconCompat.createWithResource(mActivity, R.drawable.empty_icon);
-        final PendingIntent pendingIntent = PendingIntent.getActivity(
-                mActivity,
-                TITLE.hashCode() /* requestCode */,
-                new Intent("test action"),
-                0  /* flags */);
-        final SliceAction action
-                = SliceAction.createDeeplink(pendingIntent, icon, ListBuilder.SMALL_IMAGE, TITLE);
-        return new ListBuilder(mActivity, TEST_SLICE_URI, ListBuilder.INFINITY)
-                .addRow(new ListBuilder.RowBuilder()
-                        .addEndItem(icon, ListBuilder.ICON_IMAGE)
-                        .setTitle(TITLE)
-                        .setSubtitle(SUMMARY)
-                        .setPrimaryAction(action))
-                .build();
-    }
-}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
index 8338ae4..3e5934b 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,7 +33,6 @@
 import com.android.settings.R;
 import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
 import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer.ConditionalCardHolder;
-import com.android.settings.homepage.contextualcards.slices.SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder;
 import com.android.settings.homepage.contextualcards.slices.SliceFullCardRendererHelper.SliceViewHolder;
 
 import org.junit.Before;
@@ -77,12 +74,6 @@
     }
 
     @Test
-    public void getMovementFlags_deferredSetupViewHolder_shouldDisableSwipe() {
-        assertThat(mDismissalDelegate.getMovementFlags(mRecyclerView, getDeferredSetupViewHolder()))
-                .isEqualTo(0);
-    }
-
-    @Test
     public void getMovementFlags_dismissalView_shouldDisableSwipe() {
         final RecyclerView.ViewHolder holder = getSliceViewHolder();
         holder.itemView.findViewById(R.id.dismissal_view).setVisibility(View.VISIBLE);
@@ -126,13 +117,4 @@
 
         return viewHolder;
     }
-
-    private RecyclerView.ViewHolder getDeferredSetupViewHolder() {
-        final View view = LayoutInflater.from(mActivity)
-                .inflate(VIEW_TYPE_DEFERRED_SETUP, mRecyclerView, false);
-        final RecyclerView.ViewHolder viewHolder = spy(new DeferredSetupCardViewHolder(view));
-        doReturn(VIEW_TYPE_DEFERRED_SETUP).when(viewHolder).getItemViewType();
-
-        return viewHolder;
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
index 85fc9a2..5afd4c4 100644
--- a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
+++ b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
@@ -62,7 +62,7 @@
 
     @Test
     public void getAvailableSubscriptions_nullInfoFromSubscriptionManager_nonNullResult() {
-        when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(null);
+        when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(null);
         final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
         assertThat(subs).isNotNull();
         assertThat(subs).isEmpty();
@@ -71,7 +71,7 @@
     @Test
     public void getAvailableSubscriptions_oneSubscription_oneResult() {
         final SubscriptionInfo info = mock(SubscriptionInfo.class);
-        when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info));
+        when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info));
         final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
         assertThat(subs).isNotNull();
         assertThat(subs).hasSize(1);
@@ -81,7 +81,7 @@
     public void getAvailableSubscriptions_twoSubscriptions_twoResults() {
         final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
         final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
-        when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2));
+        when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2));
         final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
         assertThat(subs).isNotNull();
         assertThat(subs).hasSize(2);
@@ -106,7 +106,7 @@
         when(info3.getSimSlotIndex()).thenReturn(0);
         when(info3.getCardString()).thenReturn("info3_cardid");
 
-        when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1));
+        when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1));
         when(mSubMgr.getAllSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2, info3));
 
         final UiccSlotInfo info2slot = mock(UiccSlotInfo.class);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
index 714eb4e..3e0bfa3 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
@@ -34,6 +35,7 @@
 
 import com.android.ims.ImsManager;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.ims.VolteQueryImsState;
 import com.android.settingslib.RestrictedSwitchPreference;
 
 import org.junit.Before;
@@ -61,6 +63,8 @@
     @Mock
     private ProvisioningManager mProvisioningManager;
 
+    private VolteQueryImsState mQueryImsState;
+
     private Enhanced4gLtePreferenceController mController;
     private SwitchPreference mPreference;
     private PersistableBundle mCarrierConfig;
@@ -81,15 +85,18 @@
         mCarrierConfig = new PersistableBundle();
         doReturn(mCarrierConfig).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
 
+        mQueryImsState = spy(new VolteQueryImsState(mContext, SUB_ID));
+
         mPreference = new RestrictedSwitchPreference(mContext);
-        mController = new Enhanced4gLtePreferenceController(mContext, "roaming") {
+        mController = spy(new Enhanced4gLtePreferenceController(mContext, "roaming") {
             @Override
             ProvisioningManager getProvisioningManager(int subId) {
                 return mProvisioningManager;
             }
-        };
+        });
         mController.init(SUB_ID);
         mController.mImsManager = mImsManager;
+        doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
         mPreference.setKey(mController.getPreferenceKey());
     }
 
@@ -114,6 +121,7 @@
 
     @Test
     public void updateState_configEnabled_prefEnabled() {
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         mPreference.setEnabled(false);
         mCarrierConfig.putInt(CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 1);
         mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
@@ -127,6 +135,7 @@
 
     @Test
     public void updateState_configOn_prefChecked() {
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         mPreference.setChecked(false);
         doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
         doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
diff --git a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
index b61504a..c9abe23 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
@@ -22,6 +22,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -31,6 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -45,6 +47,7 @@
 
 import com.android.ims.ImsManager;
 import com.android.settings.R;
+import com.android.settings.network.ims.VolteQueryImsState;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.SettingsSliceProvider;
 import com.android.settings.slices.SliceBroadcastReceiver;
@@ -52,6 +55,7 @@
 import com.android.settings.testutils.FakeFeatureFactory;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -59,11 +63,14 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowSubscriptionManager;
 
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
 public class Enhanced4gLteSliceHelperTest {
+    private static final int SUB_ID = 1;
 
     @Mock
     private CarrierConfigManager mMockCarrierConfigManager;
@@ -73,6 +80,9 @@
     @Mock
     private ProvisioningManager mProvisioningManager;
 
+    private ShadowSubscriptionManager mShadowSubscriptionManager;
+    private VolteQueryImsState mQueryImsState;
+
     private Context mContext;
     private FakeEnhanced4gLteSliceHelper mEnhanced4gLteSliceHelper;
     private SettingsSliceProvider mProvider;
@@ -85,6 +95,10 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
+        mShadowSubscriptionManager = Shadow.extract(mContext.getSystemService(
+                SubscriptionManager.class));
+        mShadowSubscriptionManager.setDefaultVoiceSubscriptionId(SUB_ID);
+
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
 
@@ -96,7 +110,10 @@
         //setup for SliceBroadcastReceiver test
         mReceiver = spy(new SliceBroadcastReceiver());
 
-        mEnhanced4gLteSliceHelper = new FakeEnhanced4gLteSliceHelper(mContext);
+        mQueryImsState = spy(new VolteQueryImsState(mContext, SUB_ID));
+
+        mEnhanced4gLteSliceHelper = spy(new FakeEnhanced4gLteSliceHelper(mContext));
+        doReturn(mQueryImsState).when(mEnhanced4gLteSliceHelper).queryImsState(anyInt());
 
         // Set-up specs for SliceMetadata.
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -104,7 +121,7 @@
 
     @Test
     public void test_CreateEnhanced4gLteSlice_invalidSubId() {
-        mEnhanced4gLteSliceHelper.setDefaultVoiceSubId(-1);
+        mShadowSubscriptionManager.setDefaultVoiceSubscriptionId(-1);
 
         final Slice slice = mEnhanced4gLteSliceHelper.createEnhanced4gLteSlice(
                 CustomSliceRegistry.ENHANCED_4G_SLICE_URI);
@@ -129,7 +146,7 @@
         when(mProvisioningManager.getProvisioningStatusForCapability(
                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
-        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
 
@@ -146,7 +163,7 @@
         when(mProvisioningManager.getProvisioningStatusForCapability(
                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
-        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
         when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
@@ -159,12 +176,13 @@
     }
 
     @Test
+    @Ignore
     public void test_SliceBroadcastReceiver_toggleOffEnhanced4gLte() {
         when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(true);
         when(mProvisioningManager.getProvisioningStatusForCapability(
                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
-        when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(false);
+        doReturn(false).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
                 .thenReturn(mEnhanced4gLteSliceHelper);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
index 4cc8f36..ba37c30 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -24,8 +24,8 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
@@ -79,9 +79,8 @@
         doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
         doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
         doReturn(SUB_ID).when(mSubscriptionInfo).getSubscriptionId();
-        doReturn(new ArrayList<>(Arrays.asList(mSubscriptionInfo)))
-                .when(mSubscriptionManager).getSelectableSubscriptionInfoList();
-
+        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                Arrays.asList(mSubscriptionInfo));
 
         // Set-up specs for SliceMetadata.
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -172,7 +171,7 @@
 
     @Test
     public void isMobileDataAvailable_noSubscriptions_slicePrimaryActionIsEmpty() {
-        doReturn(new ArrayList<>()).when(mSubscriptionManager).getSelectableSubscriptionInfoList();
+        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(new ArrayList<>());
         final Slice mobileData = mMobileDataSlice.getSlice();
 
         assertThat(mobileData).isNull();
@@ -180,7 +179,7 @@
 
     @Test
     public void isMobileDataAvailable_nullSubscriptions_slicePrimaryActionIsEmpty() {
-        doReturn(null).when(mSubscriptionManager).getSelectableSubscriptionInfoList();
+        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null);
         final Slice mobileData = mMobileDataSlice.getSlice();
 
         assertThat(mobileData).isNull();
diff --git a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
index 7bbca28..e68a0f4 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
@@ -35,6 +35,8 @@
 import androidx.preference.SwitchPreference;
 
 import com.android.ims.ImsManager;
+import com.android.settings.network.ims.VolteQueryImsState;
+import com.android.settings.network.ims.VtQueryImsState;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -59,6 +61,9 @@
     @Mock
     private PreferenceScreen mPreferenceScreen;
 
+    private VtQueryImsState mQueryImsState;
+    private VolteQueryImsState mQueryVoLteState;
+
     private VideoCallingPreferenceController mController;
     private PersistableBundle mCarrierConfig;
     private SwitchPreference mPreference;
@@ -80,15 +85,23 @@
                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
         doReturn(mCarrierConfig).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
 
+        mQueryImsState = spy(new VtQueryImsState(mContext, SUB_ID));
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
+
+        mQueryVoLteState = spy(new VolteQueryImsState(mContext, SUB_ID));
+        doReturn(true).when(mQueryVoLteState).isEnabledByUser();
+
         mPreference = new SwitchPreference(mContext);
-        mController = new VideoCallingPreferenceController(mContext, "wifi_calling") {
+        mController = spy(new VideoCallingPreferenceController(mContext, "wifi_calling") {
             @Override
             ProvisioningManager getProvisioningManager(int subId) {
                 return mProvisioningManager;
             }
-        };
+        });
         mController.init(SUB_ID);
         mController.mImsManager = mImsManager;
+        doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
+        doReturn(mQueryVoLteState).when(mController).queryVoLteState(anyInt());
         mPreference.setKey(mController.getPreferenceKey());
 
         doReturn(true).when(mImsManager).isVtEnabledByPlatform();
@@ -124,7 +137,8 @@
 
     @Test
     public void updateState_4gLteOff_disabled() {
-        doReturn(false).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+        doReturn(false).when(mQueryImsState).isEnabledByUser();
+        doReturn(false).when(mQueryVoLteState).isEnabledByUser();
 
         mController.updateState(mPreference);
 
@@ -134,8 +148,8 @@
 
     @Test
     public void updateState_4gLteOnWithoutCall_checked() {
-        doReturn(true).when(mImsManager).isVtEnabledByUser();
-        doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
+        doReturn(true).when(mQueryVoLteState).isEnabledByUser();
         doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
         mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
 
diff --git a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
index ffd0eb8..961f8cb 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
@@ -19,6 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -38,6 +40,7 @@
 import com.android.ims.ImsManager;
 import com.android.internal.R;
 import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -63,6 +66,8 @@
     @Mock
     private PreferenceScreen mPreferenceScreen;
 
+    private WifiCallingQueryImsState mQueryImsState;
+
     private WifiCallingPreferenceController mController;
     private Preference mPreference;
     private Context mContext;
@@ -74,6 +79,9 @@
 
         mContext = spy(RuntimeEnvironment.application);
 
+        mQueryImsState = spy(new WifiCallingQueryImsState(mContext, SUB_ID));
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
+
         mPreference = new Preference(mContext);
         mController = spy(new WifiCallingPreferenceController(mContext, "wifi_calling") {
             @Override
@@ -85,6 +93,7 @@
         mController.init(SUB_ID);
         mController.mImsManager = mImsManager;
         mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
+        doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
         mPreference.setKey(mController.getPreferenceKey());
 
         when(mController.getTelephonyManager(mContext, SUB_ID)).thenReturn(mTelephonyManager);
@@ -99,7 +108,7 @@
     @Test
     public void updateState_noSimCallManager_setCorrectSummary() {
         mController.mSimCallManager = null;
-        when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mImsMmTelManager.getVoWiFiRoamingModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
         when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
@@ -140,7 +149,7 @@
                 ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
         when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
-        when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mTelephonyManager.isNetworkRoaming()).thenReturn(true);
 
         mController.updateState(mPreference);
@@ -157,7 +166,7 @@
                 ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
         when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
-        when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mTelephonyManager.isNetworkRoaming()).thenReturn(true);
 
         mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
index 6fb2c49..03a7146 100644
--- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java
+++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
@@ -243,6 +243,16 @@
     }
 
     /**
+     * Assert no slice item contains title.
+     *
+     * @param sliceItems All slice items of a Slice.
+     * @param title Title for asserting.
+     */
+    public static void assertNoSliceItemContainsTitle(List<SliceItem> sliceItems, String title) {
+        assertThat(hasText(sliceItems, title, HINT_TITLE)).isFalse();
+    }
+
+    /**
      * Assert any slice item contains subtitle.
      *
      * @param sliceItems All slice items of a Slice.
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index cf96aba..c1f33c6 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -18,9 +18,11 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArraySet;
 
 import com.android.settings.Utils;
 
@@ -40,6 +42,7 @@
     private static Map<String, String> sAppNameMap;
     private static boolean sIsSystemAlertWindowEnabled;
     private static boolean sIsVoiceCapable;
+    private static ArraySet<String> sResultLinks = new ArraySet<>();
 
     @Implementation
     protected static int enforceSameOwner(Context context, int userId) {
@@ -60,6 +63,7 @@
         sIsUserAMonkey = false;
         sIsDemoUser = false;
         sIsVoiceCapable = false;
+        sResultLinks = new ArraySet<>();
     }
 
     public static void setIsDemoUser(boolean isDemoUser) {
@@ -134,4 +138,13 @@
     public static void setIsVoiceCapable(boolean isVoiceCapable) {
         sIsVoiceCapable = isVoiceCapable;
     }
+
+    @Implementation
+    protected static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
+        return sResultLinks;
+    }
+
+    public static void setHandledDomains(ArraySet<String> links) {
+        sResultLinks = links;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
index 17f8d36..ae236bf 100644
--- a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
@@ -18,8 +18,11 @@
 
 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.spy;
+import static org.mockito.Mockito.when;
 
 import android.app.settings.SettingsEnums;
 import android.net.wifi.WifiConfiguration;
@@ -32,10 +35,15 @@
 import androidx.annotation.NonNull;
 
 import com.android.settings.R;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
 
 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.shadows.androidx.fragment.FragmentController;
 
@@ -49,6 +57,9 @@
     private static final String FAKE_NEW_OPEN_SSID = "fake_new_open_ssid";
     private static final String FAKE_NEW_OPEN_SSID_WITH_QUOTE = "\"fake_new_open_ssid\"";
     private static final String FAKE_NEW_SAVED_WPA_SSID = "\"fake_new_wpa_ssid\"";
+    private static final String KEY_SSID = "key_ssid";
+    private static final String KEY_SECURITY = "key_security";
+    private static final int SCANED_LEVEL = 4;
 
     private AddAppNetworksFragment mAddAppNetworksFragment;
     private List<WifiNetworkSuggestion> mFakedSpecifiedNetworksList;
@@ -59,8 +70,15 @@
     private Bundle mBundle;
     private ArrayList<Integer> mFakedResultArrayList = new ArrayList<>();
 
+    @Mock
+    private AccessPoint mMockAccessPoint;
+
+    @Mock
+    private WifiTracker mMockWifiTracker;
+
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mAddAppNetworksFragment = spy(new AddAppNetworksFragment());
         mNewWpaSuggestionEntry = generateRegularWifiSuggestion(FAKE_NEW_WPA_SSID,
                 WifiConfiguration.KeyMgmt.WPA_PSK, "1234567890");
@@ -68,6 +86,10 @@
                 WifiConfiguration.KeyMgmt.NONE, null);
         mSavedWpaConfigurationEntry = generateRegularWifiConfiguration(FAKE_NEW_SAVED_WPA_SSID,
                 WifiConfiguration.KeyMgmt.WPA_PSK, "\"1234567890\"");
+        mAddAppNetworksFragment.mWifiTracker = mMockWifiTracker;
+        WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+
+        setUpOneScannedNetworkWithScanedLevel();
     }
 
     @Test
@@ -138,7 +160,7 @@
         assertThat(mAddAppNetworksFragment.mResultCodeArrayList.get(0)).isEqualTo(
                 mAddAppNetworksFragment.RESULT_NETWORK_ALREADY_EXISTS);
         assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(
-                0).mWifiNetworkSuggestion.wifiConfiguration.SSID).isEqualTo(
+                0).mWifiNetworkSuggestion.getWifiConfiguration().SSID).isEqualTo(
                 FAKE_NEW_OPEN_SSID_WITH_QUOTE);
     }
 
@@ -165,6 +187,52 @@
         assertThat(mAddAppNetworksFragment.mUiToRequestedList).hasSize(3);
     }
 
+    @Test
+    public void withOneSuggestion_uiElementShouldHaveInitLevel() {
+        // Arrange
+        // Setup a fake saved network list and assign to fragment.
+        addOneSavedNetworkConfiguration(mSavedWpaConfigurationEntry);
+        // Setup one specified networks and its results and assign to fragment.
+        addOneSpecifiedRegularNetworkSuggestion(mNewOpenSuggestionEntry);
+        mAddAppNetworksFragment.mAllSpecifiedNetworksList = mFakedSpecifiedNetworksList;
+
+        // Act
+        mAddAppNetworksFragment.filterSavedNetworks(mFakeSavedNetworksList);
+
+        // Assert
+        assertThat(mAddAppNetworksFragment.mUiToRequestedList).hasSize(1);
+        assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(0).mLevel).isEqualTo(
+                mAddAppNetworksFragment.INITIAL_RSSI_SIGNAL_LEVEL);
+    }
+
+    @Test
+    public void withOneSuggestion_whenScanResultChanged_uiListShouldHaveNewLevel() {
+        // Arrange
+        // Setup a fake saved network list and assign to fragment.
+        addOneSavedNetworkConfiguration(mSavedWpaConfigurationEntry);
+        // Setup one specified networks and its results and assign to fragment.
+        addOneSpecifiedRegularNetworkSuggestion(mNewOpenSuggestionEntry);
+        mAddAppNetworksFragment.mAllSpecifiedNetworksList = mFakedSpecifiedNetworksList;
+        // Call filterSavedNetworks to generate necessary objects.
+        mAddAppNetworksFragment.filterSavedNetworks(mFakeSavedNetworksList);
+
+        // Act
+        mAddAppNetworksFragment.onAccessPointsChanged();
+
+        // Assert
+        assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(0).mLevel).isEqualTo(
+                SCANED_LEVEL);
+    }
+
+    private void setUpOneScannedNetworkWithScanedLevel() {
+        final ArrayList list = new ArrayList<>();
+        list.add(mMockAccessPoint);
+        when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+        when(mMockAccessPoint.getSsidStr()).thenReturn(FAKE_NEW_OPEN_SSID);
+        when(mMockAccessPoint.matches(any(WifiConfiguration.class))).thenReturn(true);
+        when(mMockAccessPoint.getLevel()).thenReturn(SCANED_LEVEL);
+    }
+
     private void addOneSavedNetworkConfiguration(@NonNull WifiConfiguration wifiConfiguration) {
         if (mFakeSavedNetworksList == null) {
             mFakeSavedNetworksList = new ArrayList<>();
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index eb9a58d..5b76adc 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -44,7 +44,6 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ProvisioningManager;
 import android.view.View;
 import android.widget.TextView;
 
@@ -60,6 +59,7 @@
 import com.android.settings.widget.ToggleSwitch;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -189,14 +189,15 @@
     public void onResumeOnPause_provisioningCallbackRegistration() throws Exception {
         // Verify that provisioning callback is registered after call to onResume().
         mFragment.onResume();
-        verify(mImsConfig).addConfigCallback(any(ProvisioningManager.Callback.class));
+        verify(mFragment).registerProvisioningChangedCallback();
 
         // Verify that provisioning callback is unregistered after call to onPause.
         mFragment.onPause();
-        verify(mImsConfig).removeConfigCallback(any());
+        verify(mFragment).unregisterProvisioningChangedCallback();
     }
 
     @Test
+    @Ignore
     public void onResume_useWfcHomeModeConfigFalseAndEditable_shouldShowWfcRoaming() {
         // Call onResume to update the WFC roaming preference.
         mFragment.onResume();
@@ -327,7 +328,7 @@
         verify(mPreferenceScreen).addPreference(mButtonWfcRoamingMode);
         verify(mPreferenceScreen).addPreference(mUpdateAddress);
         // Check the WFC enable request.
-        verify(mImsManager).setWfcSetting(true);
+        verify(mImsMmTelManager).setVoWiFiSettingEnabled(true);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
index 274ce34..aaff22a 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
@@ -47,6 +47,7 @@
 
 import com.android.ims.ImsManager;
 import com.android.settings.R;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
 import com.android.settings.slices.CustomSliceRegistry;
 import com.android.settings.slices.SettingsSliceProvider;
 import com.android.settings.slices.SliceBroadcastReceiver;
@@ -68,6 +69,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class WifiCallingSliceHelperTest {
+    private static final int SUB_ID = 1;
 
     private Context mContext;
     @Mock
@@ -79,6 +81,8 @@
     @Mock
     private ImsMmTelManager mMockImsMmTelManager;
 
+    private WifiCallingQueryImsState mQueryImsState;
+
     private FakeWifiCallingSliceHelper mWfcSliceHelper;
     private SettingsSliceProvider mProvider;
     private SliceBroadcastReceiver mReceiver;
@@ -101,7 +105,11 @@
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
 
+        mQueryImsState = spy(new WifiCallingQueryImsState(mContext, SUB_ID));
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
+
         mWfcSliceHelper = spy(new FakeWifiCallingSliceHelper(mContext));
+        doReturn(mQueryImsState).when(mWfcSliceHelper).queryImsState(anyInt());
 
         // Set-up specs for SliceMetadata.
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -137,7 +145,7 @@
          */
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+        doReturn(false).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(false);
         when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
         mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent
@@ -155,7 +163,7 @@
     public void test_CreateWifiCallingSlice_success() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
 
@@ -170,7 +178,7 @@
     public void test_SettingSliceProvider_getsRightSliceWifiCalling() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
         when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext))
@@ -186,7 +194,7 @@
     public void test_SliceBroadcastReceiver_toggleOnWifiCalling() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+        doReturn(false).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext))
                 .thenReturn(mWfcSliceHelper);
@@ -201,7 +209,7 @@
         // change the setting
         mReceiver.onReceive(mContext, intent);
 
-        verify((mMockImsManager)).setWfcSetting(mWfcSettingCaptor.capture());
+        verify((mMockImsMmTelManager)).setVoWiFiSettingEnabled(mWfcSettingCaptor.capture());
 
         // assert the change
         assertThat(mWfcSettingCaptor.getValue()).isTrue();
@@ -211,7 +219,7 @@
     public void test_CreateWifiCallingPreferenceSlice_prefNotEditable() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         mWfcSliceHelper.setIsWifiCallingPrefEditable(false);
 
@@ -226,7 +234,7 @@
     public void test_CreateWifiCallingPreferenceSlice_wfcOff() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+        doReturn(false).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         mWfcSliceHelper.setIsWifiCallingPrefEditable(true);
 
@@ -243,7 +251,7 @@
     public void test_CreateWifiCallingPreferenceSlice_success() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -261,7 +269,7 @@
     public void test_SettingsSliceProvider_getWfcPreferenceSlice() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -280,7 +288,7 @@
     public void test_SliceBroadcastReceiver_setWfcPrefCellularPref() {
         when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
         when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
-        when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+        doReturn(true).when(mQueryImsState).isEnabledByUser();
         when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
         when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
                 ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -455,6 +463,10 @@
             return true;
         }
 
+        WifiCallingQueryImsState queryImsState(int subId) {
+            return super.queryImsState(subId);
+        }
+
         @Override
         protected Intent getWifiCallingCarrierActivityIntent(int subId) {
             return mActivationAppIntent;
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
index d39228b..6593ec3 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settings.wifi.slice;
 
+import static com.android.settings.wifi.slice.ContextualWifiSlice.COLLAPSED_ROW_COUNT;
+import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
@@ -75,75 +78,87 @@
         mWifiManager.setWifiEnabled(true);
 
         mWifiSlice = new ContextualWifiSlice(mContext);
-        mWifiSlice.sPreviouslyDisplayed = false;
     }
 
     @Test
-    public void getWifiSlice_hasActiveConnection_shouldReturnNull() {
-        mWifiSlice.sPreviouslyDisplayed = false;
-        connectToWifi(makeValidatedNetworkCapabilities());
-
-        final Slice wifiSlice = mWifiSlice.getSlice();
-
-        assertThat(wifiSlice).isNull();
-    }
-
-    @Test
-    public void getWifiSlice_newSession_hasActiveConnection_shouldReturnNull() {
-        // Session: use a non-active value
-        // previous displayed: yes
-        mWifiSlice.sPreviouslyDisplayed = true;
+    public void getWifiSlice_newSession_hasActiveConnection_shouldCollapseSlice() {
         mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
         connectToWifi(makeValidatedNetworkCapabilities());
 
         final Slice wifiSlice = mWifiSlice.getSlice();
 
-        assertThat(wifiSlice).isNull();
+        assertTitleAndIcon(wifiSlice);
+        assertNoToggle(wifiSlice);
+        assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
     }
 
     @Test
-    public void getWifiSlice_previousDisplayed_hasActiveConnection_shouldHaveTitleAndToggle() {
+    public void getWifiSlice_newSession_noConnection_shouldExpandSlice() {
+        mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+
+        final Slice wifiSlice = mWifiSlice.getSlice();
+
+        assertTitleAndIcon(wifiSlice);
+        assertToggle(wifiSlice);
+        assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
+    }
+
+    @Test
+    public void getWifiSlice_previousExpanded_hasActiveConnection_shouldExpandSlice() {
         mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
-        mWifiSlice.sPreviouslyDisplayed = true;
+        mWifiSlice.sToggleNeeded = true;
         connectToWifi(makeValidatedNetworkCapabilities());
 
         final Slice wifiSlice = mWifiSlice.getSlice();
 
-        final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice);
-        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
-
-        final List<SliceAction> toggles = metadata.getToggles();
-        assertThat(toggles).hasSize(1);
-
-        final SliceAction primaryAction = metadata.getPrimaryAction();
-        final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
-                R.drawable.ic_settings_wireless);
-        assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+        assertTitleAndIcon(wifiSlice);
+        assertToggle(wifiSlice);
+        assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
     }
 
     @Test
-    public void getWifiSlice_isCaptivePortal_shouldHaveTitleAndToggle() {
-        mWifiSlice.sPreviouslyDisplayed = false;
-        connectToWifi(WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+    public void getWifiSlice_previousExpanded_disableWifi_shouldHaveToggle() {
+        mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+        mWifiSlice.sToggleNeeded = true;
+        connectToWifi(makeValidatedNetworkCapabilities());
 
+        mWifiManager.setWifiEnabled(false);
         final Slice wifiSlice = mWifiSlice.getSlice();
 
-        final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice);
-        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
+        assertTitleAndIcon(wifiSlice);
+        assertToggle(wifiSlice);
+    }
 
-        final List<SliceAction> toggles = metadata.getToggles();
-        assertThat(toggles).hasSize(1);
+    @Test
+    public void getWifiSlice_previousCollapsed_disableWifi_shouldHaveToggle() {
+        mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+        mWifiSlice.sToggleNeeded = false;
+        connectToWifi(makeValidatedNetworkCapabilities());
 
-        final SliceAction primaryAction = metadata.getPrimaryAction();
-        final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
-                R.drawable.ic_settings_wireless);
-        assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+        mWifiManager.setWifiEnabled(false);
+        final Slice wifiSlice = mWifiSlice.getSlice();
+
+        assertTitleAndIcon(wifiSlice);
+        assertToggle(wifiSlice);
+    }
+
+    @Test
+    public void getWifiSlice_previousCollapsed_connectionLoss_shouldCollapseSlice() {
+        mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+        mWifiSlice.sToggleNeeded = false;
+        connectToWifi(makeValidatedNetworkCapabilities());
+
+        mWifiManager.disconnect();
+        final Slice wifiSlice = mWifiSlice.getSlice();
+
+        assertTitleAndIcon(wifiSlice);
+        assertNoToggle(wifiSlice);
+        assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
     }
 
     @Test
     public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() {
         mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
-        mWifiSlice.sPreviouslyDisplayed = true;
 
         final Slice wifiSlice = mWifiSlice.getSlice();
 
@@ -165,4 +180,26 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
         return nc;
     }
+
+    private void assertTitleAndIcon(Slice slice) {
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
+
+        final SliceAction primaryAction = metadata.getPrimaryAction();
+        final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
+                R.drawable.ic_settings_wireless);
+        assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+    }
+
+    private void assertToggle(Slice slice) {
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).hasSize(1);
+    }
+
+    private void assertNoToggle(Slice slice) {
+        final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+        final List<SliceAction> toggles = metadata.getToggles();
+        assertThat(toggles).isEmpty();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index 18ed006..ad74a32 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -51,9 +51,6 @@
 import com.android.settings.testutils.SliceTester;
 import com.android.settingslib.wifi.AccessPoint;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -63,12 +60,16 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = WifiSliceTest.ShadowSliceBackgroundWorker.class)
 public class WifiSliceTest {
 
     private static final String AP1_NAME = "ap1";
     private static final String AP2_NAME = "ap2";
+    private static final String AP3_NAME = "ap3";
 
     private Context mContext;
     private ContentResolver mResolver;
@@ -205,15 +206,15 @@
     }
 
     @Test
-    public void getWifiSlice_oneReachableAp_shouldNotReturnLoadingRow() {
+    public void getWifiSlice_oneReachableAp_shouldReturnLoadingRow() {
         setWorkerResults(createAccessPoint(AP1_NAME, false, true));
 
         final Slice wifiSlice = mWifiSlice.getSlice();
         final List<SliceItem> sliceItems = wifiSlice.getItems();
 
         SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
-        // No scanning text
-        SliceTester.assertNoSliceItemContainsSubtitle(sliceItems,
+        // Has scanning text
+        SliceTester.assertAnySliceItemContainsSubtitle(sliceItems,
                 mContext.getString(R.string.wifi_empty_list_wifi_on));
     }
 
@@ -221,13 +222,15 @@
     public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() {
         setWorkerResults(
                 createAccessPoint(AP1_NAME, false, true),
-                createAccessPoint(AP2_NAME, false, true));
+                createAccessPoint(AP2_NAME, false, true),
+                createAccessPoint(AP3_NAME, false, true));
 
         final Slice wifiSlice = mWifiSlice.getSlice();
         final List<SliceItem> sliceItems = wifiSlice.getItems();
 
         SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
         SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME);
+        SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP3_NAME);
         // No scanning text
         SliceTester.assertNoSliceItemContainsSubtitle(sliceItems,
                 mContext.getString(R.string.wifi_empty_list_wifi_on));