Merge "Migrate media output switcher metrics - 3/n"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e917b5c..9b8327c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3644,6 +3644,25 @@
             </intent-filter>/>
         </receiver>
 
+        <receiver
+            android:name=".sim.receivers.SimSlotChangeReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver
+            android:name=".sim.receivers.SimCompleteBootReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name=".sim.SimNotificationService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" />
+
         <!-- This is the longest AndroidManifest.xml ever. -->
     </application>
 </manifest>
diff --git a/res/drawable/ic_arrow_downward.xml b/res/drawable/ic_arrow_downward.xml
new file mode 100644
index 0000000..0def170
--- /dev/null
+++ b/res/drawable/ic_arrow_downward.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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"
+        android:viewportHeight="24"
+        android:tint="?android:attr/colorAccent">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_undo_24.xml b/res/drawable/ic_undo_24.xml
new file mode 100644
index 0000000..0a8e149
--- /dev/null
+++ b/res/drawable/ic_undo_24.xml
@@ -0,0 +1,25 @@
+<!--
+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"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M14.1,8H7.83l2.59,-2.59L9,4 4,9l5,5 1.41,-1.41L7.83,10h6.27c2.15,0 3.9,1.57 3.9,3.5S16.25,17 14.1,17H7v2h7.1c3.25,0 5.9,-2.47 5.9,-5.5S17.35,8 14.1,8z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/wifi_friction.xml b/res/drawable/wifi_friction.xml
index fa8268d..6e9010f 100644
--- a/res/drawable/wifi_friction.xml
+++ b/res/drawable/wifi_friction.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+        xmlns:settings="http://schemas.android.com/apk/res-auto">
     <item
         settings:state_encrypted="true"
         android:drawable="@drawable/ic_friction_lock_closed"/>
diff --git a/res/drawable/wifi_signal.xml b/res/drawable/wifi_signal.xml
index 7854075..fba7792 100644
--- a/res/drawable/wifi_signal.xml
+++ b/res/drawable/wifi_signal.xml
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+        xmlns:settings="http://schemas.android.com/apk/res-auto">
     <item settings:state_encrypted="true">
         <layer-list>
             <item>
diff --git a/res/layout/app_authentication_item.xml b/res/layout/app_authentication_item.xml
index 2df923f..423722e 100644
--- a/res/layout/app_authentication_item.xml
+++ b/res/layout/app_authentication_item.xml
@@ -30,6 +30,7 @@
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_toRightOf="@id/app_icon"
+        android:layout_toLeftOf="@id/expand"
         android:layout_marginTop="16dp"
         android:layout_marginBottom="16dp"
         android:orientation="vertical">
@@ -40,6 +41,13 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"/>
 
+        <TextView
+            android:id="@+id/number_of_uris"
+            style="@style/AppAuthenticationPolicyNumberOfUrisText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="gone"/>
+
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/uris"
             android:layout_width="wrap_content"
@@ -47,4 +55,11 @@
 
     </LinearLayout>
 
+    <ImageView
+        android:id="@+id/expand"
+        style="@style/AppAuthenticationExpander"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:visibility="gone"/>
+
 </RelativeLayout>
diff --git a/res/layout/biometric_enroll_layout.xml b/res/layout/biometric_enroll_layout.xml
index bf41fcc..2f3b34c 100644
--- a/res/layout/biometric_enroll_layout.xml
+++ b/res/layout/biometric_enroll_layout.xml
@@ -18,7 +18,6 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:BiometricEnrollCheckbox="http://schemas.android.com/apk/res/com.android.settings"
     style="?attr/face_layout_theme"
     app:sucHeaderText="@string/multi_biometric_enroll_title"
     android:id="@+id/setup_wizard_layout"
@@ -59,9 +58,9 @@
             android:id="@+id/checkbox_face"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            BiometricEnrollCheckbox:title="@string/multi_biometric_enroll_face_unlock_title"
-            BiometricEnrollCheckbox:description="@string/multi_biometric_enroll_face_unlock_description"
-            BiometricEnrollCheckbox:icon="@drawable/ic_face_24dp"/>
+            app:title="@string/multi_biometric_enroll_face_unlock_title"
+            app:description="@string/multi_biometric_enroll_face_unlock_description"
+            app:icon="@drawable/ic_face_24dp"/>
 
         <include layout="@layout/horizontal_divider"/>
 
@@ -69,9 +68,9 @@
             android:id="@+id/checkbox_fingerprint"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            BiometricEnrollCheckbox:title="@string/multi_biometric_enroll_fingerprint_unlock_title"
-            BiometricEnrollCheckbox:description="@string/multi_biometric_enroll_fingerprint_unlock_description"
-            BiometricEnrollCheckbox:icon="@drawable/ic_fingerprint_24dp"/>
+            app:title="@string/multi_biometric_enroll_fingerprint_unlock_title"
+            app:description="@string/multi_biometric_enroll_fingerprint_unlock_description"
+            app:icon="@drawable/ic_fingerprint_24dp"/>
 
         <include layout="@layout/horizontal_divider"/>
 
diff --git a/res/layout/credential_management_app_policy.xml b/res/layout/credential_management_app_policy.xml
new file mode 100644
index 0000000..15153e9
--- /dev/null
+++ b/res/layout/credential_management_app_policy.xml
@@ -0,0 +1,31 @@
+<!--
+  ~ 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="5dip"
+    android:orientation="vertical">
+
+    <androidx.recyclerview.widget.RecyclerView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="8dp"
+        android:scrollbars="vertical"/>
+
+</LinearLayout>
diff --git a/res/layout/face_enroll_education.xml b/res/layout/face_enroll_education.xml
index d7b95b6..d94e7c6 100644
--- a/res/layout/face_enroll_education.xml
+++ b/res/layout/face_enroll_education.xml
@@ -18,7 +18,6 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:FaceEnrollAccessibilitySwitch="http://schemas.android.com/apk/res/com.android.settings"
     style="?attr/face_layout_theme"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
@@ -94,7 +93,7 @@
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:visibility="gone"
-                    FaceEnrollAccessibilitySwitch:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
+                    app:messageText="@string/security_settings_face_enroll_introduction_accessibility_diversity"/>
 
             </FrameLayout>
 
diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml
index 55ac6f9..826f5fe 100644
--- a/res/layout/face_enroll_introduction.xml
+++ b/res/layout/face_enroll_introduction.xml
@@ -18,7 +18,6 @@
 <com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:FaceEnrollAccessibilitySwitch="http://schemas.android.com/apk/res/com.android.settings"
     style="?attr/face_layout_theme"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
diff --git a/res/layout/request_manage_credentials.xml b/res/layout/request_manage_credentials.xml
index eb4c9e8..a2350ac 100644
--- a/res/layout/request_manage_credentials.xml
+++ b/res/layout/request_manage_credentials.xml
@@ -59,4 +59,17 @@
 
     </RelativeLayout>
 
+    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
+        android:id="@+id/extended_fab"
+        style="@style/RequestManageCredentialsFab"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/request_manage_credentials_more"
+        app:layout_anchor="@id/apps_list"
+        app:layout_anchorGravity="bottom|center"
+        app:elevation="3dp"
+        app:icon="@drawable/ic_arrow_downward"
+        app:iconTint="?android:attr/colorAccent"
+        app:backgroundTint="?android:attr/colorPrimary"/>
+
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/res/layout/udfps_enroll_enrolling.xml b/res/layout/udfps_enroll_enrolling.xml
new file mode 100644
index 0000000..03b6528
--- /dev/null
+++ b/res/layout/udfps_enroll_enrolling.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.
+  -->
+
+<com.google.android.setupdesign.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout="@layout/sud_glif_blank_template"
+    style="?attr/fingerprint_layout_theme">
+
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/udfps_enroll_layout.xml b/res/layout/udfps_enroll_layout.xml
new file mode 100644
index 0000000..51c788b
--- /dev/null
+++ b/res/layout/udfps_enroll_layout.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2015 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.android.settings.biometrics.fingerprint.UdfpsEnrollLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/setup_wizard_layout"
+    style="?attr/fingerprint_layout_theme"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ImageView
+        android:id="@+id/sud_layout_icon"
+        style="@style/SudGlifIcon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="?attr/sudGlifHeaderGravity"
+        android:layout_marginEnd="0dp"
+        android:layout_marginStart="0dp"
+        android:src="@drawable/ic_fingerprint_header" />
+
+    <TextView
+        android:id="@+id/suc_layout_title"
+        style="@style/SudGlifHeaderTitle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="0dp"
+        android:layout_marginStart="0dp" />
+
+    <Space
+        android:id="@+id/space_below_title"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <FrameLayout
+        android:id="@+id/description_area"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:id="@+id/sud_layout_description"
+            style="@style/SudDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/security_settings_fingerprint_enroll_start_message" />
+
+        <TextView
+            android:id="@+id/repeat_message"
+            style="@style/SudDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/security_settings_fingerprint_enroll_repeat_message"
+            android:visibility="invisible" />
+
+    </FrameLayout>
+
+    <TextView
+        android:id="@+id/error_text"
+        style="@style/TextAppearance.ErrorText"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginBottom="16dp"
+        android:accessibilityLiveRegion="polite"
+        android:gravity="center_horizontal"
+        android:paddingEnd="5dp"
+        android:paddingStart="5dp"
+        android:visibility="invisible" />
+
+    <Space
+        android:id="@+id/space_above_animation"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <include
+        layout="@layout/fingerprint_enroll_enrolling_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center" />
+
+</com.android.settings.biometrics.fingerprint.UdfpsEnrollLayout>
diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml
index 5c5291f..638f2f9 100644
--- a/res/values-ca/arrays.xml
+++ b/res/values-ca/arrays.xml
@@ -418,7 +418,7 @@
     <item msgid="747238414788976867">"Personalitzat"</item>
   </string-array>
   <string-array name="vpn_types_long">
-    <item msgid="6621806338070912611">"VPN de PPTP"</item>
+    <item msgid="6621806338070912611">"VPN PPTP"</item>
     <item msgid="2552427673212085780">"VPN L2TP/IPSec amb claus prèviament compartides"</item>
     <item msgid="7378096704485168082">"VPN L2TP/IPSec amb certificats"</item>
     <item msgid="3792393562235791509">"VPN IPSec amb claus prèviament compartides i autenticació Xauth"</item>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 7a6e0aa..3d73f64 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -20,4 +20,5 @@
     <integer name="job_anomaly_config_update">101</integer>
     <integer name="job_anomaly_detection">102</integer>
     <integer name="device_index_update">103</integer>
+    <integer name="sim_notification_send">104</integer>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 041dbdd..8aeefdb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2289,8 +2289,8 @@
     <string name="wifi_saved_access_points_label">Saved networks</string>
     <!-- Tab title for showing subscribed WiFi access points. [CHAR LIMIT=20] -->
     <string name="wifi_subscribed_access_points_tab">Subscriptions</string>
-    <!-- Tab title for showing saved WiFi access points. -->
-    <string name="wifi_saved_access_points_tab">@string/wifi_access_points</string>
+    <!-- Tab title for showing saved other networks. -->
+    <string name="wifi_saved_other_networks_tab">Other networks</string>
     <!-- Wifi Advanced settings.  Used as a label under the shortcut icon that goes to Wifi advanced settings. [CHAR LIMIT=20] -->
     <string name="wifi_advanced_settings_label">IP settings</string>
     <!-- Error message for users that aren't allowed to see or modify WiFi advanced settings [CHAR LIMIT=NONE] -->
@@ -5720,9 +5720,9 @@
     <!-- Summary for the battery high usage tip, which presents battery may run out earlier [CHAR LIMIT=NONE] -->
     <string name="battery_tip_high_usage_summary">Battery may run out earlier than usual</string>
     <!-- Title for the battery limited temporarily tip [CHAR LIMIT=NONE] -->
-    <string name="battery_tip_limited_temporarily_title">Battery limited temporarily</string>
+    <string name="battery_tip_limited_temporarily_title">Preserving battery health</string>
     <!-- Summary for the battery limited temporarily tip [CHAR LIMIT=NONE] -->
-    <string name="battery_tip_limited_temporarily_summary">Helps preserve battery health. Tap to learn more.</string>
+    <string name="battery_tip_limited_temporarily_summary">Battery limited temporarily. Tap to learn more.</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
     <string name="battery_tip_dialog_message" product="default">Your phone has been used more than usual. Your battery may run out sooner than expected.\n\nTop apps by battery usage:</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
@@ -6335,6 +6335,14 @@
     <string name="request_manage_credentials_allow">Allow</string>
     <!-- Label for floating action button to scroll to the end of the authentication policy list [CHAR LIMIT=30] -->
     <string name="request_manage_credentials_more">Show more</string>
+    <!-- Title of preference for the certificate management app [CHAR LIMIT=30] -->
+    <string name="certificate_management_app">Certificate management app</string>
+    <!-- Summary if there is no certificate management app [CHAR_LIMIT=NONE] -->
+    <string name="no_certificate_management_app">None</string>
+    <!-- Summary of preference if there is a certificate management app [CHAR LIMIT=NONE] -->
+    <string name="certificate_management_app_description">Certificates installed by this app identify you to the apps and URLs below</string>
+    <!-- Label for button to remove the credential management app [CHAR LIMIT=30] -->
+    <string name="remove_credential_management_app">Remove</string>
 
     <!-- Sound settings screen, setting check box label -->
     <string name="emergency_tone_title">Emergency dialing signal</string>
@@ -7262,6 +7270,8 @@
     <string name="help_url_sound" translatable="false"></string>
     <!-- Help URL, Battery [DO NOT TRANSLATE] -->
     <string name="help_url_battery" translatable="false"></string>
+    <!-- Help URL, Battery Defender [DO NOT TRANSLATE] -->
+    <string name="help_url_battery_defender" translatable="false"></string>
     <!-- Help URL, Accounts [DO NOT TRANSLATE] -->
     <string name="help_url_accounts" translatable="false"></string>
     <!-- Help URL, Choose lockscreen [DO NOT TRANSLATE] -->
@@ -11649,7 +11659,7 @@
     <string name="pref_title_network_details">Network details</string>
 
     <!--  Warning text about the visibility of device name. [CHAR LIMIT=NONE] -->
-    <string name="about_phone_device_name_warning">Your device name is visible to apps on your phone. It may also be seen by other people when you connect to Bluetooth devices or set up a Wi-Fi hotspot.</string>
+    <string name="about_phone_device_name_warning">Your device name is visible to apps on your phone. It may also be seen by other people when you connect to Bluetooth devices, connect to a Wi-Fi network or set up a Wi-Fi hotspot.</string>
 
     <!-- Title for Connected device shortcut [CHAR LIMIT=30] -->
     <string name="devices_title">Devices</string>
@@ -12083,6 +12093,17 @@
     <!-- Body text of DSDS activation failure dialog. Users could toggle the selected SIM again or reboot to recover. [CHAR LIMIT=NONE] -->
     <string name="dsds_activation_failure_body_msg2">Try turning on the SIM again. If the problem continues, restart your device.</string>
 
+    <!--  Strings for SIM push notifications  -->
+    <!-- Category name of the notifications related to SIM setup. [CHAR LIMIT=NONE] -->
+    <string name="sim_setup_channel_id">Network activation</string>
+    <!--  The title of post DSDS reboot notification. The title includes carrier's name. [CHAR LIMIT=NONE] -->
+    <string name="post_dsds_reboot_notification_title_with_carrier"><xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g> is active</string>
+    <!--  The body text of post DSDS reboot notification. [CHAR LIMIT=NONE] -->
+    <string name="post_dsds_reboot_notification_text">Tap to update SIM settings</string>
+
+    <!-- Button label of the removable sim card. [CHAR LIMIT=NONE] -->
+    <string name="sim_card_label">SIM card</string>
+
     <!-- Strings for deleting eUICC subscriptions dialog activity -->
     <!-- Title on confirmation dialog asking the user if they want to erase the downloaded SIM from the device. [CHAR_LIMIT=NONE] -->
     <string name="erase_sim_dialog_title">Erase this downloaded SIM?</string>
@@ -12480,6 +12501,10 @@
     <string name="network_and_internet_preferences_summary">Connect to public networks</string>
     <!-- Search keywords for "Internet" settings [CHAR_LIMIT=NONE] -->
     <string name="keywords_internet">network connection, internet, wireless, data, wifi, wi-fi, wi fi, cellular, mobile, cell carrier, 4g, 3g, 2g, lte</string>
+    <!-- Label text to view airplane-safe networks. [CHAR LIMIT=40] -->
+    <string name="view_airplane_safe_networks">View airplane-safe networks</string>
+    <!-- Label text to turn off airplane mode. [CHAR LIMIT=40] -->
+    <string name="turn_off_airplane_mode">Turn off Airplane Mode</string>
 
     <!-- Summary for preference when Bedtime mode is on [CHAR LIMIT=NONE] -->
     <string name="aware_summary_when_bedtime_on">Unavailable because bedtime mode is on</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c447dec..fd4d268 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -805,6 +805,13 @@
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
+    <style name="RequestManageCredentialsFab">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:layout_marginBottom">12dp</item>
+    </style>
+
     <style name="RequestManageCredentialsHeader">
         <item name="android:paddingStart">40dp</item>
         <item name="android:paddingEnd">24dp</item>
@@ -816,13 +823,13 @@
     <style name="RequestManageCredentialsTitle">
         <item name="android:layout_marginTop">24dp</item>
         <item name="android:textSize">36sp</item>
-        <item name="android:textColor">#202124</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="RequestManageCredentialsDescription">
         <item name="android:layout_marginTop">24dp</item>
         <item name="android:textSize">18sp</item>
-        <item name="android:textColor">#202124</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="AppAuthenticationPolicyItem">
@@ -838,12 +845,25 @@
     <style name="AppAuthenticationPolicyText">
         <item name="android:maxLines">1</item>
         <item name="android:textSize">20sp</item>
-        <item name="android:textColor">#202124</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="AppAuthenticationPolicyNumberOfUrisText">
+        <item name="android:layout_marginTop">4dp</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:alpha">0.7</item>
+    </style>
+
+    <style name="AppAuthenticationExpander">
+        <item name="android:layout_marginTop">24dp</item>
+        <item name="android:layout_alignParentEnd">true</item>
+        <item name="android:scaleType">fitEnd</item>
     </style>
 
     <style name="AppUriAuthenticationPolicyText">
         <item name="android:maxLines">1</item>
         <item name="android:textSize">16sp</item>
-        <item name="android:textColor">#5F6368</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 </resources>
diff --git a/res/xml/credential_management_app_fragment.xml b/res/xml/credential_management_app_fragment.xml
new file mode 100644
index 0000000..9392414
--- /dev/null
+++ b/res/xml/credential_management_app_fragment.xml
@@ -0,0 +1,47 @@
+<?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/certificate_management_app">
+
+    <!-- Header -->
+    <com.android.settingslib.widget.LayoutPreference
+        android:key="header"
+        android:layout="@layout/settings_entity_header"
+        android:selectable="false"
+        android:order="-10000"
+        settings:allowDividerBelow="true"
+        settings:controller="com.android.settings.security.CredentialManagementAppHeaderController"/>
+
+    <!-- Buttons -->
+    <com.android.settingslib.widget.ActionButtonsPreference
+        android:key="buttons"
+        android:selectable="true"
+        android:order="-9999"
+        settings:allowDividerAbove="true"
+        settings:allowDividerBelow="true"
+        settings:controller="com.android.settings.security.CredentialManagementAppButtonsController"/>
+
+    <!-- Authentication Policy -->
+    <PreferenceCategory
+        android:key="authentication_policy"
+        android:layout="@layout/preference_category_no_label"
+        android:title="@string/summary_placeholder"
+        settings:controller="com.android.settings.security.CredentialManagementAppPolicyController"/>
+
+</PreferenceScreen>
diff --git a/res/xml/encryption_and_credential.xml b/res/xml/encryption_and_credential.xml
index fe0498d..f107b58 100644
--- a/res/xml/encryption_and_credential.xml
+++ b/res/xml/encryption_and_credential.xml
@@ -69,6 +69,13 @@
 
         </com.android.settingslib.RestrictedPreference>
 
+        <Preference
+            android:key="certificate_management_app"
+            android:title="@string/certificate_management_app"
+            android:summary="@string/summary_placeholder"
+            android:fragment="com.android.settings.security.CredentialManagementAppFragment"
+            settings:controller="com.android.settings.security.CredentialManagementAppPreferenceController"/>
+
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index a079478..20cf2be 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -57,18 +57,17 @@
         android:key="airplane_mode"
         android:title="@string/airplane_mode"
         android:icon="@drawable/ic_airplanemode_active"
-        android:disableDependentsState="true"
         android:order="-5"
         settings:controller="com.android.settings.network.AirplaneModePreferenceController"
         settings:userRestriction="no_airplane_mode"/>
 
-    <SwitchPreference
+    <com.android.settingslib.RestrictedSwitchPreference
         android:key="airplane_safe_networks"
         android:title="@string/airplane_safe_networks"
         android:icon="@drawable/ic_airplanemode_active"
-        android:order="-4"
         android:summary="@string/airplane_safe_networks_summary"
-        settings:controller="com.android.settings.network.AirplaneSafeNetworkModePreferenceController"
+        android:order="-4"
+        settings:userRestriction="no_airplane_mode"
         settings:keywords="@string/keywords_airplane_safe_networks" />
 
     <com.android.settingslib.RestrictedPreference
diff --git a/res/xml/network_provider_settings.xml b/res/xml/network_provider_settings.xml
index c0f2edd..c374469 100644
--- a/res/xml/network_provider_settings.xml
+++ b/res/xml/network_provider_settings.xml
@@ -28,6 +28,12 @@
         android:layout="@layout/preference_category_no_label"/>
 
     <PreferenceCategory
+        android:key="provider_model_mobile_network"
+        android:title="@string/summary_placeholder"
+        android:layout="@layout/preference_category_no_label"
+        settings:controller="com.android.settings.network.NetworkMobileProviderController"/>
+
+    <PreferenceCategory
         android:key="access_points"
         android:layout="@layout/preference_category_no_label"/>
 
diff --git a/res/xml/transcode_settings.xml b/res/xml/transcode_settings.xml
index c6fbdfd..65f6cd8 100644
--- a/res/xml/transcode_settings.xml
+++ b/res/xml/transcode_settings.xml
@@ -22,10 +22,20 @@
     settings:searchable="false">
 
     <SwitchPreference
+        android:key="transcode_user_control"
+        android:title="@string/transcode_user_control"
+        settings:controller="com.android.settings.development.transcode.TranscodeUserControlPreferenceController" />
+
+    <SwitchPreference
         android:key="transcode_enable_all"
         android:title="@string/transcode_enable_all"
         settings:controller="com.android.settings.development.transcode.TranscodeGlobalTogglePreferenceController" />
 
+    <SwitchPreference
+        android:key="transcode_default"
+        android:title="@string/transcode_default"
+        settings:controller="com.android.settings.development.transcode.TranscodeDefaultOptionPreferenceController" />
+
     <PreferenceCategory
         android:key="transcode_skip_apps"
         android:title="@string/transcode_skip_apps"
diff --git a/res/xml/wifi_display_saved_access_points2.xml b/res/xml/wifi_display_saved_access_points2.xml
index 02c732a..8cbea66 100644
--- a/res/xml/wifi_display_saved_access_points2.xml
+++ b/res/xml/wifi_display_saved_access_points2.xml
@@ -27,7 +27,7 @@
 
     <PreferenceCategory
         android:key="saved_access_points_category"
-        android:title="@string/wifi_saved_access_points_tab"
+        android:title="@string/wifi_saved_other_networks_tab"
         settings:controller="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsPreferenceController2"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/PointerSpeedPreference.java b/src/com/android/settings/PointerSpeedPreference.java
index d06a374..d48d26c 100644
--- a/src/com/android/settings/PointerSpeedPreference.java
+++ b/src/com/android/settings/PointerSpeedPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -37,6 +39,7 @@
     private boolean mRestoredOldState;
 
     private boolean mTouchInProgress;
+    private int mLastProgress = -1;
 
     private ContentObserver mSpeedObserver = new ContentObserver(new Handler()) {
         @Override
@@ -76,6 +79,10 @@
         if (!mTouchInProgress) {
             mIm.tryPointerSpeed(progress + InputManager.MIN_POINTER_SPEED);
         }
+        if (progress != mLastProgress) {
+            seekBar.performHapticFeedback(CLOCK_TICK);
+            mLastProgress = progress;
+        }
     }
 
     public void onStartTrackingTouch(SeekBar seekBar) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 588a2db..b226133 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -23,6 +23,7 @@
 
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.enterprise.EnterprisePrivacySettings;
+import com.android.settings.overlay.FeatureFactory;
 
 /**
  * Top-level Settings activity
@@ -213,7 +214,11 @@
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
-            if (!EnterprisePrivacySettings.isPageEnabled(this)) {
+            if (FeatureFactory.getFactory(this)
+                    .getEnterprisePrivacyFeatureProvider(this)
+                    .showParentalControls()) {
+                finish();
+            } else if (!EnterprisePrivacySettings.isPageEnabled(this)) {
                 finish();
             }
         }
diff --git a/src/com/android/settings/accessibility/BalanceSeekBar.java b/src/com/android/settings/accessibility/BalanceSeekBar.java
index 15526b6..8a88d6c 100644
--- a/src/com/android/settings/accessibility/BalanceSeekBar.java
+++ b/src/com/android/settings/accessibility/BalanceSeekBar.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.accessibility;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
 import static com.android.settings.Utils.isNightMode;
 
 import android.content.Context;
@@ -44,6 +45,7 @@
     private final Context mContext;
     private final Object mListenerLock = new Object();
     private OnSeekBarChangeListener mOnSeekBarChangeListener;
+    private int mLastProgress = -1;
     private final OnSeekBarChangeListener mProxySeekBarListener = new OnSeekBarChangeListener() {
         @Override
         public void onStopTrackingTouch(SeekBar seekBar) {
@@ -73,6 +75,12 @@
                     progress = mCenter;
                     seekBar.setProgress(progress); // direct update (fromUser becomes false)
                 }
+                if (progress != mLastProgress) {
+                    if (progress == mCenter || progress == getMin() || progress == getMax()) {
+                        seekBar.performHapticFeedback(CLOCK_TICK);
+                    }
+                    mLastProgress = progress;
+                }
                 final float balance = (progress - mCenter) * 0.01f;
                 Settings.System.putFloatForUser(mContext.getContentResolver(),
                         Settings.System.MASTER_BALANCE, balance, UserHandle.USER_CURRENT);
@@ -152,10 +160,5 @@
         canvas.restore();
         super.onDraw(canvas);
     }
-
-    @VisibleForTesting
-    OnSeekBarChangeListener getProxySeekBarListener() {
-        return mProxySeekBarListener;
-    }
 }
 
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
index e25bb1e..2126109 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
@@ -46,7 +46,7 @@
 
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.FilterTouchesSwitchPreference;
+import com.android.settings.widget.AppSwitchPreference;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
@@ -168,32 +168,32 @@
         if (mFooterPreference != null) {
             mFooterPreference.setVisible(mAdmins.isEmpty());
         }
-        final Map<String, FilterTouchesSwitchPreference> preferenceCache = new ArrayMap<>();
+        final Map<String, AppSwitchPreference> preferenceCache = new ArrayMap<>();
         final Context prefContext = mPreferenceGroup.getContext();
         final int childrenCount = mPreferenceGroup.getPreferenceCount();
         for (int i = 0; i < childrenCount; i++) {
             final Preference pref = mPreferenceGroup.getPreference(i);
-            if (!(pref instanceof FilterTouchesSwitchPreference)) {
+            if (!(pref instanceof AppSwitchPreference)) {
                 continue;
             }
-            final FilterTouchesSwitchPreference appSwitch = (FilterTouchesSwitchPreference) pref;
+            final AppSwitchPreference appSwitch = (AppSwitchPreference) pref;
             preferenceCache.put(appSwitch.getKey(), appSwitch);
         }
         for (DeviceAdminListItem item : mAdmins) {
             final String key = item.getKey();
-            FilterTouchesSwitchPreference pref = preferenceCache.remove(key);
+            AppSwitchPreference pref = preferenceCache.remove(key);
             if (pref == null) {
-                pref = new FilterTouchesSwitchPreference(prefContext);
+                pref = new AppSwitchPreference(prefContext);
                 mPreferenceGroup.addPreference(pref);
             }
             bindPreference(item, pref);
         }
-        for (FilterTouchesSwitchPreference unusedCacheItem : preferenceCache.values()) {
+        for (AppSwitchPreference unusedCacheItem : preferenceCache.values()) {
             mPreferenceGroup.removePreference(unusedCacheItem);
         }
     }
 
-    private void bindPreference(DeviceAdminListItem item, FilterTouchesSwitchPreference pref) {
+    private void bindPreference(DeviceAdminListItem item, AppSwitchPreference pref) {
         pref.setKey(item.getKey());
         pref.setTitle(item.getName());
         pref.setIcon(item.getIcon());
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
index fc85f7d..5da2990 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 
 import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
 
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -39,10 +40,12 @@
         return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
     }
 
-    public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+    public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label,
+            Fragment target) {
         Bundle args = new Bundle();
         args.putString(KEY_PKG, pkg);
         args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+        setTargetFragment(target, 0);
         setArguments(args);
         return this;
     }
@@ -58,6 +61,8 @@
                 R.string.zen_access_revoke_warning_dialog_title, label);
         final String summary = getResources()
                 .getString(R.string.zen_access_revoke_warning_dialog_summary);
+
+        ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
         return new AlertDialog.Builder(getContext())
                 .setMessage(summary)
                 .setTitle(title)
@@ -66,6 +71,7 @@
                         (dialog, id) -> {
                             ZenAccessController.deleteRules(getContext(), pkg);
                             ZenAccessController.setAccess(getContext(), pkg, false);
+                            parent.refreshUi();
                         })
                 .setNegativeButton(R.string.cancel,
                         (dialog, id) -> {
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
index 778206b..e4ef48b 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 
 import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
 
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -38,10 +39,11 @@
         return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
     }
 
-    public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+    public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label, Fragment target) {
         Bundle args = new Bundle();
         args.putString(KEY_PKG, pkg);
         args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+        setTargetFragment(target, 0);
         setArguments(args);
         return this;
     }
@@ -57,12 +59,18 @@
                 label);
         final String summary = getResources()
                 .getString(R.string.zen_access_warning_dialog_summary);
+
+        ZenAccessDetails parent = (ZenAccessDetails) getTargetFragment();
+
         return new AlertDialog.Builder(getContext())
                 .setMessage(summary)
                 .setTitle(title)
                 .setCancelable(true)
                 .setPositiveButton(R.string.allow,
-                        (dialog, id) -> ZenAccessController.setAccess(getContext(), pkg, true))
+                        (dialog, id) -> {
+                            ZenAccessController.setAccess(getContext(), pkg, true);
+                            parent.refreshUi();
+                        })
                 .setNegativeButton(R.string.deny,
                         (dialog, id) -> {
                             // pass
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
index ba6bb1d..c608b5b 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -79,11 +79,11 @@
             final boolean access = (Boolean) newValue;
             if (access) {
                 new ScaryWarningDialogFragment()
-                        .setPkgInfo(mPackageName, label)
+                        .setPkgInfo(mPackageName, label, ZenAccessDetails.this)
                         .show(getFragmentManager(), "dialog");
             } else {
                 new FriendlyWarningDialogFragment()
-                        .setPkgInfo(mPackageName, label)
+                        .setPkgInfo(mPackageName, label, ZenAccessDetails.this)
                         .show(getFragmentManager(), "dialog");
             }
             return false;
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
index da238f6..f378e7b 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
@@ -54,11 +54,6 @@
     @Override
     public void onStart() {
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(
-                        Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES),
-                false /* notifyForDescendants */,
-                this /* observer */);
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS),
                 false /* notifyForDescendants */,
                 this /* observer */);
diff --git a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
index f131e35..9b86e78 100644
--- a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.biometrics;
 
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.os.UserHandle;
 import android.view.View;
@@ -33,6 +34,7 @@
 
     private static final String TAG_SIDECAR = "sidecar";
 
+    @Nullable
     protected BiometricEnrollSidecar mSidecar;
 
     /**
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 813c384..b33113b 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -27,11 +27,13 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.media.AudioAttributes;
 import android.os.Bundle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.AnimationUtils;
@@ -51,11 +53,14 @@
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupdesign.util.DescriptionStyler;
 
+import java.util.List;
+
 /**
  * Activity which handles the actual enrolling for fingerprint.
  */
 public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
 
+    private static final String TAG = "FingerprintEnrollEnrolling";
     static final String TAG_SIDECAR = "sidecar";
 
     private static final int PROGRESS_BAR_MAX = 10000;
@@ -86,6 +91,7 @@
                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                     .build();
 
+    private boolean mCanAssumeUdfps;
     private ProgressBar mProgressBar;
     private ObjectAnimator mProgressAnim;
     private TextView mStartMessage;
@@ -130,14 +136,35 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.fingerprint_enroll_enrolling);
+
+        final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
+        final List<FingerprintSensorPropertiesInternal> props =
+                fingerprintManager.getSensorPropertiesInternal();
+        mCanAssumeUdfps = props.size() == 1 && props.get(0).isAnyUdfpsType();
+
+        if (mCanAssumeUdfps) {
+            // Use a custom layout since animations, etc must be based off of the sensor's physical
+            // location.
+            setContentView(R.layout.udfps_enroll_enrolling);
+            final UdfpsEnrollLayout udfpsEnrollLayout = (UdfpsEnrollLayout) getLayoutInflater()
+                    .inflate(R.layout.udfps_enroll_layout, null /* root */);
+            getLayout().addView(udfpsEnrollLayout);
+        } else {
+            setContentView(R.layout.fingerprint_enroll_enrolling);
+        }
+
         setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
-        mStartMessage = (TextView) findViewById(R.id.sud_layout_description);
-        mRepeatMessage = (TextView) findViewById(R.id.repeat_message);
-        mErrorText = (TextView) findViewById(R.id.error_text);
-        mProgressBar = (ProgressBar) findViewById(R.id.fingerprint_progress_bar);
+
+        mStartMessage = findViewById(R.id.sud_layout_description);
+        mRepeatMessage = findViewById(R.id.repeat_message);
+        mErrorText = findViewById(R.id.error_text);
+        mProgressBar = findViewById(R.id.fingerprint_progress_bar);
         mVibrator = getSystemService(Vibrator.class);
 
+        if (mCanAssumeUdfps) {
+            mProgressBar.setVisibility(View.INVISIBLE);
+        }
+
         if (getLayout().shouldApplyPartnerHeavyThemeResource()) {
             DescriptionStyler.applyPartnerCustomizationHeavyStyle(mRepeatMessage);
         } else if (getLayout().shouldApplyPartnerResource()) {
@@ -193,7 +220,7 @@
 
     @Override
     protected boolean shouldStartAutomatically() {
-        return true;
+        return !mCanAssumeUdfps;
     }
 
     @Override
@@ -209,6 +236,12 @@
     @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
+
+        if (mCanAssumeUdfps) {
+            startEnrollment();
+            mProgressBar.setVisibility(View.VISIBLE);
+        }
+
         mAnimationCancelled = false;
         startIconAnimation();
     }
@@ -252,7 +285,7 @@
     }
 
     private void updateDescription() {
-        if (mSidecar.getEnrollmentSteps() == -1) {
+        if (mSidecar == null || mSidecar.getEnrollmentSteps() == -1) {
             mStartMessage.setVisibility(View.VISIBLE);
             mRepeatMessage.setVisibility(View.INVISIBLE);
         } else {
@@ -299,6 +332,11 @@
     }
 
     private void updateProgress(boolean animate) {
+        if (mSidecar == null || !mSidecar.isEnrolling()) {
+            Log.d(TAG, "Enrollment not started yet");
+            return;
+        }
+
         int progress = getProgress(
                 mSidecar.getEnrollmentSteps(), mSidecar.getEnrollmentRemaining());
         if (animate) {
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollLayout.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollLayout.java
new file mode 100644
index 0000000..ca27e84
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollLayout.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.biometrics.fingerprint;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import com.android.settings.R;
+
+public class UdfpsEnrollLayout extends LinearLayout {
+
+    private static final String TAG = "UdfpsEnrollLayout";
+
+    private final FingerprintSensorPropertiesInternal mSensorProps;
+
+    public UdfpsEnrollLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mSensorProps = context.getSystemService(FingerprintManager.class)
+                .getSensorPropertiesInternal().get(0);
+    }
+
+    @Override
+    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        final View animation = findViewById(R.id.fingerprint_progress_bar);
+        final WindowManager wm = getContext().getSystemService(WindowManager.class);
+        final int statusbarHeight = Math.abs(wm.getCurrentWindowMetrics().getWindowInsets()
+                .getInsets(WindowInsets.Type.statusBars()).toRect().height());
+
+        // Calculate the amount of translation required. This is just re-arranged from
+        // animation.setY(mSensorProps.sensorLocationY-statusbarHeight-mSensorProps.sensorRadius)
+        // The translationY is the amount of extra height that should be added to the spacer
+        // above the animation
+        final int spaceHeight = mSensorProps.sensorLocationY - statusbarHeight
+                - mSensorProps.sensorRadius - animation.getTop();
+         animation.setTranslationY(spaceHeight);
+    }
+
+
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        final View animation = findViewById(R.id.fingerprint_progress_bar);
+        final int sensorDiameter = mSensorProps.sensorRadius * 2;
+        // Multiply it slightly so that the progress bar is outside the UDFPS affordance, and that
+        // the animation is within the UDFPS affordance.
+        final int animationDiameter = (int) (sensorDiameter * 1);
+        animation.measure(MeasureSpec.makeMeasureSpec(animationDiameter, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(animationDiameter, MeasureSpec.EXACTLY));
+    }
+}
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index 1ab3a65..7f8ade1 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -129,8 +129,6 @@
         super.displayPreference(screen);
         mLayoutPreference = screen.findPreference(getPreferenceKey());
         mLayoutPreference.setVisible(isAvailable());
-
-        refresh();
     }
 
     @Override
@@ -142,6 +140,8 @@
         mCachedDevice.registerCallback(this);
         mBluetoothAdapter.addOnMetadataChangedListener(mCachedDevice.getDevice(),
                 mContext.getMainExecutor(), mMetadataListener);
+
+        refresh();
     }
 
     @Override
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
index 4dd9a40..be383dc 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
@@ -34,6 +34,8 @@
 import com.android.internal.app.AlertController;
 import com.android.settings.R;
 
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 /**
  * BluetoothPermissionActivity shows a dialog for accepting incoming
  * profile connection request from untrusted devices.
@@ -76,6 +78,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         Intent i = getIntent();
         String action = i.getAction();
         if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
index ae3b08a..684f90f 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
@@ -131,6 +131,7 @@
                 // "Clear All Notifications" button
 
                 Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+                deleteIntent.setPackage("com.android.bluetooth");
                 deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
                 deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
                         BluetoothDevice.CONNECTION_ACCESS_NO);
diff --git a/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceController.java b/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceController.java
new file mode 100644
index 0000000..3cbf3cb
--- /dev/null
+++ b/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.transcode;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * The controller (on the transcode settings screen) indicating that by default we assume that apps
+ * support modern formats.
+ */
+public class TranscodeDefaultOptionPreferenceController extends TogglePreferenceController {
+    private static final String TRANSCODE_DEFAULT_SYS_PROP_KEY =
+            "persist.sys.fuse.transcode_default";
+
+    public TranscodeDefaultOptionPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return !SystemProperties.getBoolean(TRANSCODE_DEFAULT_SYS_PROP_KEY, false);
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        SystemProperties.set(TRANSCODE_DEFAULT_SYS_PROP_KEY, String.valueOf(!isChecked));
+        return true;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+}
diff --git a/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceController.java b/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceController.java
index 643adfc..f36f4cb 100644
--- a/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceController.java
+++ b/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceController.java
@@ -22,12 +22,12 @@
 import com.android.settings.core.TogglePreferenceController;
 
 /**
- * The controller for the "Disable transcoding for all apps" switch on the transcode settings
+ * The controller for the "Enabling transcoding for all apps" switch on the transcode settings
  * screen.
  */
 public class TranscodeGlobalTogglePreferenceController extends TogglePreferenceController {
 
-    private static final String TRANSCODE_ENABLED_PROP_KEY = "persist.sys.fuse.transcode";
+    private static final String TRANSCODE_ENABLED_PROP_KEY = "persist.sys.fuse.transcode_enabled";
 
     public TranscodeGlobalTogglePreferenceController(Context context,
             String preferenceKey) {
@@ -41,12 +41,12 @@
 
     @Override
     public boolean isChecked() {
-        return !SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, false);
+        return SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, false);
     }
 
     @Override
     public boolean setChecked(boolean isChecked) {
-        SystemProperties.set(TRANSCODE_ENABLED_PROP_KEY, String.valueOf(!isChecked));
+        SystemProperties.set(TRANSCODE_ENABLED_PROP_KEY, String.valueOf(isChecked));
         return true;
     }
 }
diff --git a/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceController.java b/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceController.java
new file mode 100644
index 0000000..49456ff
--- /dev/null
+++ b/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development.transcode;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * The controller for the User's control (over other transcoding preferences) preference switch on
+ * the transcode settings screen.
+ */
+public class TranscodeUserControlPreferenceController extends TogglePreferenceController {
+    private static final String TRANSCODE_USER_CONTROL_SYS_PROP_KEY =
+            "persist.sys.fuse.transcode_user_control";
+
+    public TranscodeUserControlPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return SystemProperties.getBoolean(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, false);
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        SystemProperties.set(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, String.valueOf(isChecked));
+        return true;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
index 8fec0c4..f733c72 100644
--- a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
@@ -29,7 +29,7 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.deletionhelper.ActivationWarningFragment;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.PrimarySwitchController;
+import com.android.settings.widget.GenericSwitchController;
 import com.android.settings.widget.PrimarySwitchPreference;
 import com.android.settings.widget.SwitchWidgetController;
 import com.android.settingslib.Utils;
@@ -44,7 +44,7 @@
     static final String STORAGE_MANAGER_ENABLED_BY_DEFAULT_PROPERTY = "ro.storage_manager.enabled";
     private final MetricsFeatureProvider mMetricsFeatureProvider;
     private PrimarySwitchPreference mSwitch;
-    private PrimarySwitchController mSwitchController;
+    private GenericSwitchController mSwitchController;
     private FragmentManager mFragmentManager;
 
     public AutomaticStorageManagementSwitchPreferenceController(Context context, String key) {
@@ -77,7 +77,7 @@
         mSwitch.setChecked(Utils.isStorageManagerEnabled(mContext));
 
         if (mSwitch != null) {
-            mSwitchController = new PrimarySwitchController(mSwitch);
+            mSwitchController = new GenericSwitchController(mSwitch);
             mSwitchController.setListener(this);
             mSwitchController.startListening();
         }
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProvider.java b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProvider.java
index 46f9b71..62f15b9 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProvider.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProvider.java
@@ -137,4 +137,10 @@
      * info" page. Returns {@code true} if the activity has indeed been launched.
      */
     boolean showWorkPolicyInfo();
+
+    /**
+     * Launches the parental controls settings page. Returns {@code true} if the activity has
+     * been launched.
+     */
+    boolean showParentalControls();
 }
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
index 429c537..7a5b489 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImpl.java
@@ -40,6 +40,9 @@
 
 public class EnterprisePrivacyFeatureProviderImpl implements EnterprisePrivacyFeatureProvider {
 
+    public static final String ACTION_PARENTAL_CONTROLS =
+            "android.settings.SHOW_PARENTAL_CONTROLS";
+
     private final Context mContext;
     private final DevicePolicyManager mDpm;
     private final PackageManager mPm;
@@ -246,6 +249,34 @@
         return false;
     }
 
+    @Override
+    public boolean showParentalControls() {
+        Intent intent = getParentalControlsIntent();
+        if (intent != null) {
+            mContext.startActivity(intent);
+            return true;
+        }
+
+        return false;
+    }
+
+    private Intent getParentalControlsIntent() {
+        final ComponentName componentName =
+                mDpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(new UserHandle(MY_USER_ID));
+        if (componentName == null) {
+            return null;
+        }
+
+        final Intent intent = new Intent(ACTION_PARENTAL_CONTROLS)
+                .setPackage(componentName.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        final List<ResolveInfo> activities = mPm.queryIntentActivitiesAsUser(intent, 0, MY_USER_ID);
+        if (activities.size() != 0) {
+            return intent;
+        }
+        return null;
+    }
+
     private ComponentName getDeviceOwnerComponent() {
         if (!mPm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
             return null;
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index 2041543..71e65bf 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.BatteryManager;
 import android.os.PowerManager;
 import android.util.Log;
 
@@ -39,6 +40,7 @@
  * 1. Battery level(e.g. 100%->99%)
  * 2. Battery status(e.g. plugged->unplugged)
  * 3. Battery saver(e.g. off->on)
+ * 4. Battery health(e.g. good->overheat)
  */
 public class BatteryBroadcastReceiver extends BroadcastReceiver {
 
@@ -49,6 +51,7 @@
      * Battery level(e.g. 100%->99%)
      * Battery status(e.g. plugged->unplugged)
      * Battery saver(e.g. off->on)
+     * Battery health(e.g. good->overheat)
      */
     public interface OnBatteryChangedListener {
         void onBatteryChanged(@BatteryUpdateType int type);
@@ -59,19 +62,23 @@
             BatteryUpdateType.BATTERY_LEVEL,
             BatteryUpdateType.BATTERY_SAVER,
             BatteryUpdateType.BATTERY_STATUS,
+            BatteryUpdateType.BATTERY_HEALTH,
             BatteryUpdateType.BATTERY_NOT_PRESENT})
     public @interface BatteryUpdateType {
         int MANUAL = 0;
         int BATTERY_LEVEL = 1;
         int BATTERY_SAVER = 2;
         int BATTERY_STATUS = 3;
-        int BATTERY_NOT_PRESENT = 4;
+        int BATTERY_HEALTH = 4;
+        int BATTERY_NOT_PRESENT = 5;
     }
 
     @VisibleForTesting
     String mBatteryLevel;
     @VisibleForTesting
     String mBatteryStatus;
+    @VisibleForTesting
+    int mBatteryHealth;
     private OnBatteryChangedListener mBatteryListener;
     private Context mContext;
 
@@ -106,11 +113,15 @@
             if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                 final String batteryLevel = Utils.getBatteryPercentage(intent);
                 final String batteryStatus = Utils.getBatteryStatus(mContext, intent);
+                final int batteryHealth = intent.getIntExtra(
+                        BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
                 if (!Utils.isBatteryPresent(intent)) {
                     Log.w(TAG, "Problem reading the battery meter.");
                     mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_NOT_PRESENT);
                 } else if (forceUpdate) {
                     mBatteryListener.onBatteryChanged(BatteryUpdateType.MANUAL);
+                } else if (batteryHealth != mBatteryHealth) {
+                    mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH);
                 } else if(!batteryLevel.equals(mBatteryLevel)) {
                     mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL);
                 } else if (!batteryStatus.equals(mBatteryStatus)) {
@@ -118,6 +129,7 @@
                 }
                 mBatteryLevel = batteryLevel;
                 mBatteryStatus = batteryStatus;
+                mBatteryHealth = batteryHealth;
             } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
                 mBatteryListener.onBatteryChanged(BatteryUpdateType.BATTERY_SAVER);
             }
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index 7b910a1..9066444 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -124,7 +124,9 @@
     public void updateHeaderPreference(BatteryInfo info) {
         mBatteryPercentText.setText(formatBatteryPercentageText(info.batteryLevel));
         if (!mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info)) {
-            if (info.remainingLabel == null) {
+            if (BatteryUtils.isBatteryDefenderOn(info)) {
+                mSummary1.setText(null);
+            } else if (info.remainingLabel == null) {
                 mSummary1.setText(info.statusLabel);
             } else {
                 mSummary1.setText(info.remainingLabel);
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 1935c35..5d7b325 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -45,6 +45,7 @@
     public CharSequence remainingLabel;
     public int batteryLevel;
     public boolean discharging = true;
+    public boolean isOverheated;
     public long remainingTimeUs = 0;
     public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN;
     public String batteryPercentString;
@@ -232,6 +233,9 @@
         info.batteryPercentString = Utils.formatPercentage(info.batteryLevel);
         info.mCharging = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
         info.averageTimeToDischarge = estimate.getAverageDischargeTime();
+        info.isOverheated = batteryBroadcast.getIntExtra(
+                BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
+                == BatteryManager.BATTERY_HEALTH_OVERHEAT;
 
         info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast);
         if (!info.mCharging) {
@@ -251,7 +255,12 @@
                 BatteryManager.BATTERY_STATUS_UNKNOWN);
         info.discharging = false;
         info.suggestionLabel = null;
-        if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+        if (info.isOverheated && status != BatteryManager.BATTERY_STATUS_FULL) {
+            info.remainingLabel = null;
+            int chargingLimitedResId = R.string.power_charging_limited;
+            info.chargeLabel =
+                context.getString(chargingLimitedResId, info.batteryPercentString);
+        } else if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
             info.remainingTimeUs = chargeTime;
             CharSequence timeString = StringUtil.formatElapsedTime(context,
                     PowerUtil.convertUsToMs(info.remainingTimeUs), false /* withSeconds */);
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 4725459..5b1f096 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -404,6 +404,13 @@
     }
 
     /**
+     * Return {@code true} if battery is overheated and charging.
+     */
+    public static boolean isBatteryDefenderOn(BatteryInfo batteryInfo) {
+        return batteryInfo.isOverheated && !batteryInfo.discharging;
+    }
+
+    /**
      * Find package uid from package name
      *
      * @param packageName used to find the uid
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index 8d6e07d..0c916b2 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -23,6 +23,7 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.EarlyWarningDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
@@ -72,6 +73,7 @@
                 batteryInfo.discharging).detect());
         tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
         tips.add(new EarlyWarningDetector(policy, context).detect());
+        tips.add(new BatteryDefenderDetector(batteryInfo).detect());
         tips.add(new SummaryDetector(policy, batteryInfo.averageTimeToDischarge).detect());
         // Disable this feature now since it introduces false positive cases. We will try to improve
         // it in the future.
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index cdefe4d..e88a494 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -29,6 +29,7 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.fuelgauge.batterytip.actions.BatteryDefenderAction;
 import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
 import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
 import com.android.settings.fuelgauge.batterytip.actions.OpenBatterySaverAction;
@@ -111,6 +112,8 @@
                 }
             case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
                 return new UnrestrictAppAction(settingsActivity, (UnrestrictAppTip) batteryTip);
+            case BatteryTip.TipType.BATTERY_DEFENDER:
+                return new BatteryDefenderAction(settingsActivity);
             default:
                 return null;
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java
new file mode 100644
index 0000000..b8f5483
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/BatteryDefenderAction.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip.actions;
+
+import android.content.Intent;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settingslib.HelpUtils;
+
+/**
+ * Action to open the Support Center article
+ */
+public class BatteryDefenderAction extends BatteryTipAction {
+    private SettingsActivity mSettingsActivity;
+
+    public BatteryDefenderAction(SettingsActivity settingsActivity) {
+        super(settingsActivity.getApplicationContext());
+        mSettingsActivity = settingsActivity;
+    }
+
+    /**
+     * Handle the action when user clicks positive button
+     */
+    @Override
+    public void handlePositiveAction(int metricsKey) {
+        final Intent intent = HelpUtils.getHelpIntent(
+                mContext,
+                mContext.getString(R.string.help_url_battery_defender),
+                getClass().getName());
+        if (intent != null) {
+            mSettingsActivity.startActivityForResult(intent, 0);
+        }
+        // TODO(b/173985153): Add logging enums for Battery Defender.
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java
new file mode 100644
index 0000000..dc33026
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetector.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip.detectors;
+
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryDefenderTip;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+
+/**
+ * Detect whether the battery is overheated
+ */
+public class BatteryDefenderDetector implements BatteryTipDetector {
+    private BatteryInfo mBatteryInfo;
+
+    public BatteryDefenderDetector(BatteryInfo batteryInfo) {
+        mBatteryInfo = batteryInfo;
+    }
+
+    @Override
+    public BatteryTip detect() {
+        final int state =
+                BatteryUtils.isBatteryDefenderOn(mBatteryInfo)
+                    ? BatteryTip.StateType.NEW
+                    : BatteryTip.StateType.INVISIBLE;
+        return new BatteryDefenderTip(state);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
new file mode 100644
index 0000000..cd23e50
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip.tips;
+
+import android.content.Context;
+import android.os.Parcel;
+
+import com.android.settings.R;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+/**
+ * Tip to show current battery is overheated
+ */
+public class BatteryDefenderTip extends BatteryTip {
+
+    public BatteryDefenderTip(@StateType int state) {
+        super(TipType.BATTERY_DEFENDER, state, false /* showDialog */);
+    }
+
+    private BatteryDefenderTip(Parcel in) {
+        super(in);
+    }
+
+    @Override
+    public CharSequence getTitle(Context context) {
+        return context.getString(R.string.battery_tip_limited_temporarily_title);
+    }
+
+    @Override
+    public CharSequence getSummary(Context context) {
+        return context.getString(R.string.battery_tip_limited_temporarily_summary);
+    }
+
+    @Override
+    public int getIconId() {
+        return R.drawable.ic_battery_status_good_24dp;
+    }
+
+    @Override
+    public void updateState(BatteryTip tip) {
+        mState = tip.mState;
+    }
+
+    @Override
+    public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) {
+        // TODO(b/173985153): Add logging enums for Battery Defender.
+    }
+
+    public static final Creator CREATOR = new Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new BatteryDefenderTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new BatteryDefenderTip[size];
+        }
+    };
+
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 12fcaba..3b849be 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -57,7 +57,8 @@
             TipType.APP_RESTRICTION,
             TipType.REDUCED_BATTERY,
             TipType.LOW_BATTERY,
-            TipType.REMOVE_APP_RESTRICTION})
+            TipType.REMOVE_APP_RESTRICTION,
+            TipType.BATTERY_DEFENDER})
     public @interface TipType {
         int SMART_BATTERY_MANAGER = 0;
         int APP_RESTRICTION = 1;
@@ -67,20 +68,22 @@
         int LOW_BATTERY = 5;
         int SUMMARY = 6;
         int REMOVE_APP_RESTRICTION = 7;
+        int BATTERY_DEFENDER = 8;
     }
 
     @VisibleForTesting
     static final SparseIntArray TIP_ORDER;
     static {
         TIP_ORDER = new SparseIntArray();
-        TIP_ORDER.append(TipType.APP_RESTRICTION, 0);
-        TIP_ORDER.append(TipType.BATTERY_SAVER, 1);
-        TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 2);
-        TIP_ORDER.append(TipType.LOW_BATTERY, 3);
-        TIP_ORDER.append(TipType.SUMMARY, 4);
-        TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 5);
-        TIP_ORDER.append(TipType.REDUCED_BATTERY, 6);
-        TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 7);
+        TIP_ORDER.append(TipType.BATTERY_DEFENDER, 0);
+        TIP_ORDER.append(TipType.APP_RESTRICTION, 1);
+        TIP_ORDER.append(TipType.BATTERY_SAVER, 2);
+        TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 3);
+        TIP_ORDER.append(TipType.LOW_BATTERY, 4);
+        TIP_ORDER.append(TipType.SUMMARY, 5);
+        TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 6);
+        TIP_ORDER.append(TipType.REDUCED_BATTERY, 7);
+        TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 8);
     }
 
     private static final String KEY_PREFIX = "key_battery_tip";
diff --git a/src/com/android/settings/network/AirplaneSafeNetworksPreferenceController.java b/src/com/android/settings/network/AirplaneSafeNetworksPreferenceController.java
new file mode 100644
index 0000000..fa80c38
--- /dev/null
+++ b/src/com/android/settings/network/AirplaneSafeNetworksPreferenceController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.content.Context;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.AirplaneModeEnabler;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.widget.GenericSwitchController;
+import com.android.settings.wifi.WifiEnabler;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class AirplaneSafeNetworksPreferenceController extends AbstractPreferenceController
+        implements LifecycleObserver, AirplaneModeEnabler.OnAirplaneModeChangedListener {
+
+    private static final String PREFERENCE_KEY = "airplane_safe_networks";
+
+    private RestrictedSwitchPreference mPreference;
+
+    private AirplaneModeEnabler mAirplaneModeEnabler;
+    private WifiEnabler mWifiEnabler;
+
+    public AirplaneSafeNetworksPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle == null) {
+            throw new IllegalArgumentException("Lifecycle must be set");
+        }
+
+        mAirplaneModeEnabler = new AirplaneModeEnabler(mContext, this);
+        lifecycle.addObserver(this);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return PREFERENCE_KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mAirplaneModeEnabler.isAirplaneModeOn();
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_START)
+    public void onStart() {
+        mAirplaneModeEnabler.start();
+        if (mPreference != null) {
+            mWifiEnabler = new WifiEnabler(mContext, new GenericSwitchController(mPreference),
+                    FeatureFactory.getFactory(mContext).getMetricsFeatureProvider());
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+    public void onStop() {
+        mAirplaneModeEnabler.stop();
+        if (mWifiEnabler != null) {
+            mWifiEnabler.teardownSwitchController();
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    public void onResume() {
+        if (mWifiEnabler != null) {
+            mWifiEnabler.resume(mContext);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    public void onPause() {
+        if (mWifiEnabler != null) {
+            mWifiEnabler.pause();
+        }
+    }
+
+    @Override
+    public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
+        if (mPreference != null) {
+            mPreference.setVisible(isAirplaneModeOn);
+        }
+    }
+}
diff --git a/src/com/android/settings/network/AirplaneSafeNetworksSlice.java b/src/com/android/settings/network/AirplaneSafeNetworksSlice.java
new file mode 100644
index 0000000..fbef282
--- /dev/null
+++ b/src/com/android/settings/network/AirplaneSafeNetworksSlice.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+import androidx.annotation.IntDef;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.settings.AirplaneModeEnabler;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.slices.CustomSliceRegistry;
+import com.android.settings.slices.CustomSliceable;
+import com.android.settings.slices.SliceBackgroundWorker;
+import com.android.settings.slices.SliceBroadcastReceiver;
+import com.android.settingslib.WirelessUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * {@link CustomSliceable} for airplane-safe networks, used by generic clients.
+ */
+// TODO(b/173413889): Need to update the slice to Button style.
+public class AirplaneSafeNetworksSlice implements CustomSliceable,
+        AirplaneModeEnabler.OnAirplaneModeChangedListener {
+
+    private static final String TAG = "AirplaneSafeNetworksSlice";
+
+    public static final String ACTION_INTENT_EXTRA = "action";
+
+    /**
+     * Annotation for different action of the slice.
+     *
+     * {@code VIEW_AIRPLANE_SAFE_NETWORKS} for action of turning on Wi-Fi.
+     * {@code TURN_OFF_AIRPLANE_MODE} for action of turning off Airplane Mode.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            Action.VIEW_AIRPLANE_SAFE_NETWORKS,
+            Action.TURN_OFF_AIRPLANE_MODE,
+    })
+    public @interface Action {
+        int VIEW_AIRPLANE_SAFE_NETWORKS = 1;
+        int TURN_OFF_AIRPLANE_MODE = 2;
+    }
+
+    private final Context mContext;
+    private final AirplaneModeEnabler mAirplaneModeEnabler;
+    private final WifiManager mWifiManager;
+
+    public AirplaneSafeNetworksSlice(Context context) {
+        mContext = context;
+        mAirplaneModeEnabler = new AirplaneModeEnabler(context, this);
+        mWifiManager = mContext.getSystemService(WifiManager.class);
+    }
+
+    private static void logd(String s) {
+        Log.d(TAG, s);
+    }
+
+    @Override
+    public Slice getSlice() {
+        if (!WirelessUtils.isAirplaneModeOn(mContext)) {
+            return null;
+        }
+
+        return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+                .addRow(new RowBuilder()
+                        .setTitle(getTitle())
+                        .setPrimaryAction(getSliceAction()))
+                .build();
+    }
+
+    @Override
+    public Uri getUri() {
+        return CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI;
+    }
+
+    @Override
+    public void onNotifyChange(Intent intent) {
+        final int action = intent.getIntExtra(ACTION_INTENT_EXTRA, 0);
+        if (action == Action.VIEW_AIRPLANE_SAFE_NETWORKS) {
+            if (!mWifiManager.isWifiEnabled()) {
+                logd("Action: turn on WiFi");
+                mWifiManager.setWifiEnabled(true);
+            }
+        } else if (action == Action.TURN_OFF_AIRPLANE_MODE) {
+            if (WirelessUtils.isAirplaneModeOn(mContext)) {
+                logd("Action: turn off Airplane mode");
+                mAirplaneModeEnabler.setAirplaneMode(false);
+            }
+        }
+    }
+
+    @Override
+    public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
+        final AirplaneSafeNetworksWorker worker = SliceBackgroundWorker.getInstance(getUri());
+        if (worker != null) {
+            worker.updateSlice();
+        }
+    }
+
+    @Override
+    public Intent getIntent() {
+        return new Intent(getUri().toString())
+                .setData(getUri())
+                .setClass(mContext, SliceBroadcastReceiver.class)
+                .putExtra(ACTION_INTENT_EXTRA, getAction());
+    }
+
+    @Action
+    private int getAction() {
+        return mWifiManager.isWifiEnabled()
+                ? Action.TURN_OFF_AIRPLANE_MODE
+                : Action.VIEW_AIRPLANE_SAFE_NETWORKS;
+    }
+
+    private String getTitle() {
+        return mContext.getText(
+                (getAction() == Action.VIEW_AIRPLANE_SAFE_NETWORKS)
+                        ? R.string.view_airplane_safe_networks
+                        : R.string.turn_off_airplane_mode).toString();
+    }
+
+    private SliceAction getSliceAction() {
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
+                0 /* requestCode */, getIntent(),
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        final IconCompat icon = Utils.createIconWithDrawable(new ColorDrawable(Color.TRANSPARENT));
+        return SliceAction.createDeeplink(pendingIntent, icon, ListBuilder.ACTION_WITH_LABEL,
+                getTitle());
+    }
+
+    @Override
+    public Class getBackgroundWorkerClass() {
+        return AirplaneSafeNetworksWorker.class;
+    }
+
+    public static class AirplaneSafeNetworksWorker extends SliceBackgroundWorker {
+
+        private final IntentFilter mIntentFilter;
+        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+                    notifySliceChange();
+                }
+            }
+        };
+
+        public AirplaneSafeNetworksWorker(Context context, Uri uri) {
+            super(context, uri);
+            mIntentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        }
+
+        @Override
+        protected void onSlicePinned() {
+            getContext().registerReceiver(mBroadcastReceiver, mIntentFilter);
+        }
+
+        @Override
+        protected void onSliceUnpinned() {
+            getContext().unregisterReceiver(mBroadcastReceiver);
+        }
+
+        @Override
+        public void close() {
+            // Do nothing.
+        }
+
+        public void updateSlice() {
+            notifySliceChange();
+        }
+    }
+}
diff --git a/src/com/android/settings/network/AllInOneTetherPreferenceController.java b/src/com/android/settings/network/AllInOneTetherPreferenceController.java
index cc55e7a..0f4905a 100644
--- a/src/com/android/settings/network/AllInOneTetherPreferenceController.java
+++ b/src/com/android/settings/network/AllInOneTetherPreferenceController.java
@@ -42,7 +42,7 @@
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.FeatureFlags;
-import com.android.settings.widget.PrimarySwitchController;
+import com.android.settings.widget.GenericSwitchController;
 import com.android.settings.widget.PrimarySwitchPreference;
 import com.android.settingslib.TetherUtil;
 
@@ -191,7 +191,7 @@
     void initEnabler(Lifecycle lifecycle) {
         if (mPreference != null) {
             mTetherEnabler = new TetherEnabler(
-                    mContext, new PrimarySwitchController(mPreference), mBluetoothPan);
+                    mContext, new GenericSwitchController(mPreference), mBluetoothPan);
             if (lifecycle != null) {
                 lifecycle.addObserver(mTetherEnabler);
             }
diff --git a/src/com/android/settings/network/MobileNetworkListController.java b/src/com/android/settings/network/MobileNetworkListController.java
index f1980b2..77c93ff 100644
--- a/src/com/android/settings/network/MobileNetworkListController.java
+++ b/src/com/android/settings/network/MobileNetworkListController.java
@@ -128,7 +128,7 @@
             pref.setOnPreferenceClickListener(clickedPref -> {
                 if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId)
                         && !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {
-                    mSubscriptionManager.setSubscriptionEnabled(subId, true);
+                    SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true);
                 } else {
                     final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
                     intent.putExtra(Settings.EXTRA_SUB_ID, info.getSubscriptionId());
diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.java b/src/com/android/settings/network/MobileNetworkSummaryController.java
index 405d365..08da41a 100644
--- a/src/com/android/settings/network/MobileNetworkSummaryController.java
+++ b/src/com/android/settings/network/MobileNetworkSummaryController.java
@@ -189,7 +189,8 @@
                     final int subId = info.getSubscriptionId();
                     if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId)
                             && !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {
-                        mSubscriptionManager.setSubscriptionEnabled(subId, true);
+                        SubscriptionUtil.startToggleSubscriptionDialogActivity(
+                                mContext, subId, true);
                     } else {
                         final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
                         intent.putExtra(Settings.EXTRA_SUB_ID, subs.get(0).getSubscriptionId());
diff --git a/src/com/android/settings/network/MultiNetworkHeaderController.java b/src/com/android/settings/network/MultiNetworkHeaderController.java
index e99cbb6..1143546 100644
--- a/src/com/android/settings/network/MultiNetworkHeaderController.java
+++ b/src/com/android/settings/network/MultiNetworkHeaderController.java
@@ -27,9 +27,14 @@
 import com.android.settings.wifi.WifiConnectionPreferenceController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
-// This controls a header at the top of the Network & internet page that only appears when there
-// are two or more active mobile subscriptions. It shows an overview of available network
-// connections with an entry for wifi (if connected) and an entry for each subscription.
+/**
+ * This controls a header at the top of the Network & internet page that only appears when there
+ * are two or more active mobile subscriptions. It shows an overview of available network
+ * connections with an entry for wifi (if connected) and an entry for each subscription.
+ *
+ * TODO(tomhsu) when provider model is completed, this class will be replaced
+ * by {@link NetworkMobileProviderController}
+ */
 public class MultiNetworkHeaderController extends BasePreferenceController implements
         WifiConnectionPreferenceController.UpdateListener,
         SubscriptionsPreferenceController.UpdateListener {
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 76a84bb..e56db27 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -147,6 +147,7 @@
         controllers.add(privateDnsPreferenceController);
         if (Utils.isProviderModelEnabled(context)) {
             controllers.add(new NetworkProviderCallsSmsController(context, lifecycle));
+            controllers.add(new AirplaneSafeNetworksPreferenceController(context, lifecycle));
         }
         return controllers;
     }
diff --git a/src/com/android/settings/network/NetworkMobileProviderController.java b/src/com/android/settings/network/NetworkMobileProviderController.java
new file mode 100644
index 0000000..4c29256
--- /dev/null
+++ b/src/com/android/settings/network/NetworkMobileProviderController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * This controls mobile network display of the internet page that only appears when there
+ * are active mobile subscriptions. It shows an overview of available mobile network
+ * connections with an entry for each subscription.
+ *
+ *  {@link NetworkMobileProviderController} is used to show subscription status on internet
+ *  page for provider model. This original class can refer to {@link MultiNetworkHeaderController},
+ *
+  */
+public class NetworkMobileProviderController extends BasePreferenceController implements
+        SubscriptionsPreferenceController.UpdateListener {
+
+    private static final String TAG = NetworkMobileProviderController.class.getSimpleName();
+
+    public static final String PREF_KEY_PROVIDER_MOBILE_NETWORK = "provider_model_mobile_network";
+    private static final int PREFERENCE_START_ORDER = 10;
+
+    private PreferenceCategory mPreferenceCategory;
+    private PreferenceScreen mPreferenceScreen;
+
+    private SubscriptionsPreferenceController mSubscriptionsController;
+
+    private int mOriginalExpandedChildrenCount;
+
+    public NetworkMobileProviderController(Context context, String key) {
+        super(context, key);
+    }
+
+    /**
+     * Initialize NetworkMobileProviderController
+     * @param lifecycle Lifecycle of Settings
+     */
+    public void init(Lifecycle lifecycle) {
+        mSubscriptionsController = createSubscriptionsController(lifecycle);
+    }
+
+    @VisibleForTesting
+    SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
+        if (mSubscriptionsController == null) {
+            return new SubscriptionsPreferenceController(
+                    mContext,
+                    lifecycle,
+                    this,
+                    PREF_KEY_PROVIDER_MOBILE_NETWORK,
+                    PREFERENCE_START_ORDER);
+        }
+        return mSubscriptionsController;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (mSubscriptionsController == null) {
+            Log.e(TAG, "[displayPreference] SubscriptionsController is null.");
+            return;
+        }
+        mPreferenceScreen = screen;
+        mOriginalExpandedChildrenCount = mPreferenceScreen.getInitialExpandedChildrenCount();
+        mPreferenceCategory = screen.findPreference(PREF_KEY_PROVIDER_MOBILE_NETWORK);
+        mPreferenceCategory.setVisible(isAvailable());
+        // TODO(tomhsu) For the provider model, subscriptionsController shall do more
+        // implementation of preference type change and summary control.
+        mSubscriptionsController.displayPreference(screen);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if (mSubscriptionsController == null) {
+            return CONDITIONALLY_UNAVAILABLE;
+        }
+        return mSubscriptionsController.isAvailable() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public void onChildrenUpdated() {
+        final boolean available = isAvailable();
+        // TODO(b/129893781) we need a better way to express where the advanced collapsing starts
+        // for preference groups that have items dynamically added/removed in the top expanded
+        // section.
+        if (mOriginalExpandedChildrenCount != Integer.MAX_VALUE) {
+            if (available) {
+                mPreferenceScreen.setInitialExpandedChildrenCount(
+                        mOriginalExpandedChildrenCount + mPreferenceCategory.getPreferenceCount());
+            } else {
+                mPreferenceScreen.setInitialExpandedChildrenCount(mOriginalExpandedChildrenCount);
+            }
+        }
+        mPreferenceCategory.setVisible(available);
+    }
+}
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index df62190..90e3ac4 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -208,6 +208,12 @@
     private LinkablePreference mStatusMessagePreference;
 
     /**
+     * Mobile networks list for provider model
+     */
+    private static final String PREF_KEY_PROVIDER_MOBILE_NETWORK = "provider_model_mobile_network";
+    private NetworkMobileProviderController mNetworkMobileProviderController;
+
+    /**
      * Tracks whether the user initiated a connection via clicking in order to autoscroll to the
      * network once connected.
      */
@@ -255,6 +261,16 @@
         mDataUsagePreference.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(),
                 0 /*subId*/,
                 null /*service*/);
+        addNetworkMobileProviderController();
+    }
+
+    private void addNetworkMobileProviderController() {
+        if (mNetworkMobileProviderController == null) {
+            mNetworkMobileProviderController = new NetworkMobileProviderController(
+                    getContext(), PREF_KEY_PROVIDER_MOBILE_NETWORK);
+        }
+        mNetworkMobileProviderController.init(getSettingsLifecycle());
+        mNetworkMobileProviderController.displayPreference(getPreferenceScreen());
     }
 
     @Override
@@ -341,6 +357,12 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+
+    }
+
+    @Override
     public void onDestroyView() {
         mWorkerThread.quit();
 
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index e2c8997..cff8f55 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -28,6 +28,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.UiccSlotInfo;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -284,14 +285,31 @@
         }
     }
 
-    /** Starts a dialog activity to handle SIM enabling/disabling. */
+    /**
+     * Starts a dialog activity to handle SIM enabling/disabling.
+     * @param context {@code Context}
+     * @param subId The id of subscription need to be enabled or disabled.
+     * @param enable Whether the subscription with {@code subId} should be enabled or disabled.
+     */
     public static void startToggleSubscriptionDialogActivity(
             Context context, int subId, boolean enable) {
+        if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
+            Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID.");
+            return;
+        }
         context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
     }
 
-    /** Starts a dialog activity to handle eSIM deletion. */
+    /**
+     * Starts a dialog activity to handle eSIM deletion.
+     * @param context {@code Context}
+     * @param subId The id of subscription need to be deleted.
+     */
     public static void startDeleteEuiccSubscriptionDialogActivity(Context context, int subId) {
+        if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
+            Log.i(TAG, "Unable to delete subscription due to invalid subscription ID.");
+            return;
+        }
         context.startActivity(DeleteEuiccSubscriptionDialogActivity.getIntent(context, subId));
     }
 
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index 53d6c30..864078c 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -41,6 +41,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.network.telephony.DataConnectivityListener;
 import com.android.settings.network.telephony.MobileNetworkActivity;
 import com.android.settings.network.telephony.MobileNetworkUtils;
@@ -77,7 +78,6 @@
     // Map of subscription id to Preference
     private Map<Integer, Preference> mSubscriptionPreferences;
     private int mStartOrder;
-
     /**
      * This interface lets a parent of this class know that some change happened - this could
      * either be because overall availability changed, or because we've added/removed/updated some
@@ -291,7 +291,7 @@
                 // subscriptions with same group UUID.
                 .filter(subInfo ->
                         isSubscriptionCanBeDisplayed(mContext, subInfo.getSubscriptionId()))
-                .count() >= 2;
+                .count() >= (Utils.isProviderModelEnabled(mContext) ? 1 : 2);
     }
 
     @Override
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
index 46c5234..1fdc191 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.java
@@ -78,8 +78,8 @@
         mSwitchBar.getSwitch().setOnBeforeCheckedChangeListener((toggleSwitch, isChecked) -> {
             // TODO b/135222940: re-evaluate whether to use
             // mSubscriptionManager#isSubscriptionEnabled
-            if (mSubscriptionManager.isActiveSubscriptionId(mSubId) != isChecked
-                    && (!mSubscriptionManager.setSubscriptionEnabled(mSubId, isChecked))) {
+            if (mSubscriptionManager.isActiveSubscriptionId(mSubId) != isChecked) {
+                SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, mSubId, isChecked);
                 return true;
             }
             return false;
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 919415b..4af42ba 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -27,6 +27,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.SidecarFragment;
 import com.android.settings.network.EnableMultiSimSidecar;
@@ -34,6 +35,7 @@
 import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
 import com.android.settings.network.SwitchToRemovableSlotSidecar;
 import com.android.settings.network.UiccSlotUtil;
+import com.android.settings.sim.SimActivationNotifier;
 
 import com.google.common.collect.ImmutableList;
 
@@ -45,7 +47,8 @@
 
     private static final String TAG = "ToggleSubscriptionDialogActivity";
     // Arguments
-    private static final String ARG_enable = "enable";
+    @VisibleForTesting
+    public static final String ARG_enable = "enable";
     // Dialog tags
     private static final int DIALOG_TAG_DISABLE_SIM_CONFIRMATION = 1;
     private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
@@ -189,9 +192,8 @@
                     return;
                 }
                 Log.i(TAG, "User confirmed reboot to enable DSDS.");
+                SimActivationNotifier.setShowSimSettingsNotification(this, true);
                 mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
-                // TODO(b/170507290): Store a bit in preferences for displaying the notification
-                //  after the reboot.
                 break;
             case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
                 Log.i(TAG, "User confirmed to enable the subscription.");
@@ -294,6 +296,7 @@
     private void handleTogglePsimAction() {
         if (mSubscriptionManager.canDisablePhysicalSubscription() && mSubInfo != null) {
             mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
+            finish();
         } else {
             Log.i(
                     TAG,
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 9df548c..65ad571 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -35,7 +35,6 @@
 import android.view.View;
 import android.widget.Toast;
 
-import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
@@ -48,6 +47,7 @@
 import com.android.settings.widget.EmptyTextSettings;
 import com.android.settingslib.applications.ServiceListing;
 import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.apppreference.AppPreference;
 
 import java.util.List;
 
@@ -140,7 +140,7 @@
                 Log.e(TAG, "can't find package name", e);
             }
 
-            final Preference pref = new Preference(getPrefContext());
+            final AppPreference pref = new AppPreference(getPrefContext());
             pref.setTitle(title);
             pref.setIcon(mIconDrawableFactory.getBadgedIcon(service, service.applicationInfo,
                     UserHandle.getUserId(service.applicationInfo.uid)));
diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
index 0eb14ba..f836855 100644
--- a/src/com/android/settings/notification/history/NotificationSbnAdapter.java
+++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
@@ -52,6 +52,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.settings.R;
+import com.android.settingslib.Utils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -79,8 +80,8 @@
         mPm = pm;
         mUserBadgeCache = new HashMap<>();
         mValues = new ArrayList<>();
-        mBackgroundColor = mContext.getColor(
-                com.android.internal.R.color.notification_material_background_color);
+        mBackgroundColor = Utils.getColorAttrDefaultColor(context,
+                android.R.attr.colorBackground);
         Configuration currentConfig = mContext.getResources().getConfiguration();
         mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                 == Configuration.UI_MODE_NIGHT_YES;
diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java
index db0c5e3..6ae7089 100644
--- a/src/com/android/settings/panel/InternetConnectivityPanel.java
+++ b/src/com/android/settings/panel/InternetConnectivityPanel.java
@@ -23,6 +23,7 @@
 import android.provider.Settings;
 
 import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.network.AirplaneModePreferenceController;
 import com.android.settings.slices.CustomSliceRegistry;
 
@@ -58,7 +59,11 @@
         final List<Uri> uris = new ArrayList<>();
         uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
         uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
-        uris.add(AirplaneModePreferenceController.SLICE_URI);
+        if (Utils.isProviderModelEnabled(mContext)) {
+            uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI);
+        } else {
+            uris.add(AirplaneModePreferenceController.SLICE_URI);
+        }
         return uris;
     }
 
diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java
index 35e369e..8b0c2c9 100644
--- a/src/com/android/settings/password/ChooseLockGeneric.java
+++ b/src/com/android/settings/password/ChooseLockGeneric.java
@@ -221,9 +221,19 @@
             mForBiometrics = intent.getBooleanExtra(
                     ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
 
-            mRequestedMinComplexity = intent
+            final int complexityFromIntent = intent
                     .getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_NONE);
-            mCallerAppName =
+            final int complexityFromAdmin = mLockPatternUtils.getRequestedPasswordComplexity(
+                    mUserId);
+            mRequestedMinComplexity = Math.max(complexityFromIntent, complexityFromAdmin);
+            final boolean isComplexityProvidedByAdmin = (complexityFromAdmin > complexityFromIntent)
+                    && mRequestedMinComplexity > PASSWORD_COMPLEXITY_NONE;
+
+            // If the complexity is provided by the admin, do not get the caller app's name.
+            // If the app requires, for example, low complexity, and the admin requires high
+            // complexity, it does not make sense to show a footer telling the user it's the app
+            // requesting a particular complexity because the admin-set complexity will override it.
+            mCallerAppName = isComplexityProvidedByAdmin ? null :
                     intent.getStringExtra(EXTRA_KEY_CALLER_APP_NAME);
             mIsCallingAppAdmin = intent
                     .getBooleanExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, /* defValue= */ false);
@@ -669,8 +679,9 @@
             final PreferenceScreen entries = getPreferenceScreen();
 
             int adminEnforcedQuality = mDpm.getPasswordQuality(null, mUserId);
-            EnforcedAdmin enforcedAdmin = RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(
-                    getActivity(), mUserId);
+            EnforcedAdmin enforcedAdmin =
+                    RestrictedLockUtilsInternal.checkIfPasswordQualityIsSet(getActivity(),
+                            mUserId);
             // If we are to unify a work challenge at the end of the credential enrollment, manually
             // merge any password policy from that profile here, so we are enrolling a compliant
             // password. This is because once unified, the profile's password policy will
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index a73b73a..0c84ba9 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -421,6 +421,8 @@
             if (mUnificationProfileId != UserHandle.USER_NULL) {
                 mMinMetrics.maxWith(
                         mLockPatternUtils.getRequestedPasswordMetrics(mUnificationProfileId));
+                mMinComplexity = Math.max(mMinComplexity,
+                        mLockPatternUtils.getRequestedPasswordComplexity(mUnificationProfileId));
             }
 
             if (intent.getBooleanExtra(
diff --git a/src/com/android/settings/security/CredentialManagementAppAdapter.java b/src/com/android/settings/security/CredentialManagementAppAdapter.java
index 707f598..e56fc63 100644
--- a/src/com/android/settings/security/CredentialManagementAppAdapter.java
+++ b/src/com/android/settings/security/CredentialManagementAppAdapter.java
@@ -59,6 +59,9 @@
     private final PackageManager mPackageManager;
     private final RecyclerView.RecycledViewPool mViewPool;
 
+    private final boolean mIncludeHeader;
+    private final boolean mIncludeExpander;
+
     /**
      * View holder for the header in the request manage credentials screen.
      */
@@ -96,13 +99,29 @@
     public class AppAuthenticationViewHolder extends RecyclerView.ViewHolder {
         private final ImageView mAppIconView;
         private final TextView mAppNameView;
-        RecyclerView mChildRecyclerView;
+        private final TextView mNumberOfUrisView;
+        private final ImageView mExpanderIconView;
+        private final RecyclerView mChildRecyclerView;
+        private final List<String> mExpandedApps;
 
         public AppAuthenticationViewHolder(View view) {
             super(view);
             mAppIconView = view.findViewById(R.id.app_icon);
             mAppNameView = view.findViewById(R.id.app_name);
+            mNumberOfUrisView = view.findViewById(R.id.number_of_uris);
+            mExpanderIconView = view.findViewById(R.id.expand);
             mChildRecyclerView = view.findViewById(R.id.uris);
+            mExpandedApps = new ArrayList<>();
+
+            mExpanderIconView.setOnClickListener(view1 -> {
+                final String appName = mSortedAppNames.get(getBindingAdapterPosition());
+                if (mExpandedApps.contains(appName)) {
+                    mExpandedApps.remove(appName);
+                } else {
+                    mExpandedApps.add(appName);
+                }
+                bindPolicyView(appName);
+            });
         }
 
         /**
@@ -119,32 +138,63 @@
                 mAppIconView.setImageDrawable(null);
                 mAppNameView.setText(appName);
             }
-            bindChildView(mAppUriAuthentication.get(appName));
+            bindPolicyView(appName);
+        }
+
+        private void bindPolicyView(String appName) {
+            if (mIncludeExpander) {
+                mExpanderIconView.setVisibility(View.VISIBLE);
+                if (mExpandedApps.contains(appName)) {
+                    mNumberOfUrisView.setVisibility(View.GONE);
+                    mExpanderIconView.setImageResource(R.drawable.ic_expand_less);
+                    bindChildView(mAppUriAuthentication.get(appName));
+                } else {
+                    mChildRecyclerView.setVisibility(View.GONE);
+                    mNumberOfUrisView.setVisibility(View.VISIBLE);
+                    mNumberOfUrisView.setText(
+                            getNumberOfUrlsText(mAppUriAuthentication.get(appName)));
+                    mExpanderIconView.setImageResource(
+                            com.android.internal.R.drawable.ic_expand_more);
+                }
+            } else {
+                mNumberOfUrisView.setVisibility(View.GONE);
+                mExpanderIconView.setVisibility(View.GONE);
+                bindChildView(mAppUriAuthentication.get(appName));
+            }
         }
 
         /**
          * Bind the list of URIs for an app.
          */
-        public void bindChildView(Map<Uri, String> urisToAliases) {
+        private void bindChildView(Map<Uri, String> urisToAliases) {
             LinearLayoutManager layoutManager = new LinearLayoutManager(
                     mChildRecyclerView.getContext(), RecyclerView.VERTICAL, false);
             layoutManager.setInitialPrefetchItemCount(urisToAliases.size());
             UriAuthenticationPolicyAdapter childItemAdapter =
                     new UriAuthenticationPolicyAdapter(new ArrayList<>(urisToAliases.keySet()));
             mChildRecyclerView.setLayoutManager(layoutManager);
+            mChildRecyclerView.setVisibility(View.VISIBLE);
             mChildRecyclerView.setAdapter(childItemAdapter);
             mChildRecyclerView.setRecycledViewPool(mViewPool);
         }
+
+        private String getNumberOfUrlsText(Map<Uri, String> urisToAliases) {
+            String url = urisToAliases.size() > 1 ? " URLs" : " URL";
+            return urisToAliases.size() + url;
+        }
     }
 
     public CredentialManagementAppAdapter(Context context, String credentialManagerPackage,
-            Map<String, Map<Uri, String>> appUriAuthentication) {
+            Map<String, Map<Uri, String>> appUriAuthentication,
+            boolean includeHeader, boolean includeExpander) {
         mContext = context;
         mCredentialManagerPackage = credentialManagerPackage;
         mPackageManager = context.getPackageManager();
         mAppUriAuthentication = appUriAuthentication;
         mSortedAppNames = sortPackageNames(mAppUriAuthentication);
         mViewPool = new RecyclerView.RecycledViewPool();
+        mIncludeHeader = includeHeader;
+        mIncludeExpander = includeExpander;
     }
 
     /**
@@ -198,19 +248,20 @@
         if (viewHolder instanceof HeaderViewHolder) {
             ((HeaderViewHolder) viewHolder).bindView();
         } else if (viewHolder instanceof AppAuthenticationViewHolder) {
-            ((AppAuthenticationViewHolder) viewHolder).bindView(i - 1);
+            int position = mIncludeHeader ? i - 1 : i;
+            ((AppAuthenticationViewHolder) viewHolder).bindView(position);
         }
     }
 
     @Override
     public int getItemCount() {
         // Add an extra view to show the header view
-        return mAppUriAuthentication.size() + 1;
+        return mIncludeHeader ? mAppUriAuthentication.size() + 1 : mAppUriAuthentication.size();
     }
 
     @Override
     public int getItemViewType(int position) {
-        if (position == 0) {
+        if (mIncludeHeader && position == 0) {
             return HEADER_VIEW;
         }
         return super.getItemViewType(position);
diff --git a/src/com/android/settings/security/CredentialManagementAppButtonsController.java b/src/com/android/settings/security/CredentialManagementAppButtonsController.java
new file mode 100644
index 0000000..9efa098
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppButtonsController.java
@@ -0,0 +1,109 @@
+/*
+ * 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.security;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.ActionButtonsPreference;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Controller that shows the remove button of the credential management app, which allows the user
+ * to remove the credential management app and its certificates.
+ */
+public class CredentialManagementAppButtonsController extends BasePreferenceController {
+
+    private static final String TAG = "CredentialManagementApp";
+
+    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private final PackageManager mPackageManager;
+    private final AppOpsManager mAppOpsManager;
+    private boolean mHasCredentialManagerPackage;
+    private String mCredentialManagerPackageName;
+
+    public CredentialManagementAppButtonsController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mPackageManager = context.getPackageManager();
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE_UNSEARCHABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mExecutor.execute(() -> {
+            try {
+                IKeyChainService service = KeyChain.bind(mContext).getService();
+                mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+                mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+            } catch (InterruptedException | RemoteException e) {
+                Log.e(TAG, "Unable to display credential management app buttons");
+            }
+            mHandler.post(() -> displayButtons(screen));
+        });
+    }
+
+    private void displayButtons(PreferenceScreen screen) {
+        if (mHasCredentialManagerPackage) {
+            ((ActionButtonsPreference) screen.findPreference(getPreferenceKey()))
+                    .setButton1Text(R.string.remove_credential_management_app)
+                    .setButton1Icon(R.drawable.ic_undo_24)
+                    .setButton1OnClickListener(view -> removeCredentialManagementApp());
+        }
+    }
+
+    private void removeCredentialManagementApp() {
+        try {
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+                    mCredentialManagerPackageName, 0);
+            mAppOpsManager.setMode(AppOpsManager.OP_MANAGE_CREDENTIALS,
+                    appInfo.uid, mCredentialManagerPackageName, AppOpsManager.MODE_DEFAULT);
+            mExecutor.execute(() -> {
+                try {
+                    IKeyChainService service = KeyChain.bind(mContext).getService();
+                    service.removeCredentialManagementApp();
+                } catch (InterruptedException | RemoteException e) {
+                    Log.e(TAG, "Unable to remove the credential management app");
+                }
+            });
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Unable to remove the credential management app");
+        }
+    }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppFragment.java b/src/com/android/settings/security/CredentialManagementAppFragment.java
new file mode 100644
index 0000000..5544ee6
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppFragment.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.security;
+
+import android.app.settings.SettingsEnums;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Settings fragment containing the credential management app. The credential management app has
+ * the ability to manage the user's credentials on unmanaged devices.
+ */
+@SearchIndexable
+public class CredentialManagementAppFragment extends DashboardFragment {
+
+    private static final String TAG = "CredentialManagementApp";
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.credential_management_app_fragment;
+    }
+
+    @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.CREDENTIAL_MANAGEMENT_APP;
+    }
+
+    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider(R.xml.credential_management_app_fragment);
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppHeaderController.java b/src/com/android/settings/security/CredentialManagementAppHeaderController.java
new file mode 100644
index 0000000..975c49d
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppHeaderController.java
@@ -0,0 +1,102 @@
+/*
+ * 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.security;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Controller that shows the header of the credential management app, which includes credential
+ * management app's name, icon and a description.
+ */
+public class CredentialManagementAppHeaderController extends BasePreferenceController {
+
+    private static final String TAG = "CredentialManagementApp";
+
+    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    public CredentialManagementAppHeaderController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+        mPackageManager = context.getPackageManager();
+    }
+
+    private final PackageManager mPackageManager;
+    private boolean mHasCredentialManagerPackage;
+    private String mCredentialManagerPackageName;
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE_UNSEARCHABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mExecutor.execute(() -> {
+            try {
+                IKeyChainService service = KeyChain.bind(mContext).getService();
+                mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+                mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+            } catch (InterruptedException | RemoteException e) {
+                Log.e(TAG, "Unable to display credential management app header");
+            }
+            mHandler.post(() -> displayHeader(screen));
+        });
+    }
+
+    private void displayHeader(PreferenceScreen screen) {
+        LayoutPreference headerPref = screen.findPreference(getPreferenceKey());
+        ImageView mAppIconView = headerPref.findViewById(R.id.entity_header_icon);
+        TextView mTitleView = headerPref.findViewById(R.id.entity_header_title);
+        TextView mDescriptionView = headerPref.findViewById(R.id.entity_header_summary);
+
+        try {
+            ApplicationInfo applicationInfo =
+                    mPackageManager.getApplicationInfo(mCredentialManagerPackageName, 0);
+            mAppIconView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo));
+            mTitleView.setText(applicationInfo.loadLabel(mPackageManager));
+        } catch (PackageManager.NameNotFoundException e) {
+            mAppIconView.setImageDrawable(null);
+            mTitleView.setText(mCredentialManagerPackageName);
+        }
+        // TODO (b/165641221): The description should be multi-lined, which is currently a
+        // limitation of using Settings entity header. However, the Settings entity header
+        // should be used to be consistent with the rest of Settings.
+        mDescriptionView.setText(
+                mContext.getString(R.string.request_manage_credentials_description));
+    }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPolicyController.java b/src/com/android/settings/security/CredentialManagementAppPolicyController.java
new file mode 100644
index 0000000..9561c5f
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPolicyController.java
@@ -0,0 +1,49 @@
+/*
+ * 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.security;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller that shows the credential management app's authentication policy.
+ */
+public class CredentialManagementAppPolicyController extends BasePreferenceController {
+
+    public CredentialManagementAppPolicyController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE_UNSEARCHABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        PreferenceGroup group = screen.findPreference(getPreferenceKey());
+        group.addPreference(new CredentialManagementAppPolicyPreference(group.getContext()));
+    }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java b/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java
new file mode 100644
index 0000000..1747be3
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPolicyPreference.java
@@ -0,0 +1,89 @@
+/*
+ * 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.security;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.AppUriAuthenticationPolicy;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Preference that shows the credential management app's authentication policy.
+ */
+public class CredentialManagementAppPolicyPreference extends Preference {
+
+    private static final String TAG = "CredentialManagementApp";
+
+    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private final Context mContext;
+
+    private boolean mHasCredentialManagerPackage;
+    private String mCredentialManagerPackageName;
+    private AppUriAuthenticationPolicy mCredentialManagerPolicy;
+
+    public CredentialManagementAppPolicyPreference(Context context) {
+        super(context);
+        setLayoutResource(R.layout.credential_management_app_policy);
+        mContext = context;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        mExecutor.execute(() -> {
+            try {
+                IKeyChainService service = KeyChain.bind(mContext).getService();
+                mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+                mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+                mCredentialManagerPolicy = service.getCredentialManagementAppPolicy();
+            } catch (InterruptedException | RemoteException e) {
+                Log.e(TAG, "Unable to display credential management app policy");
+            }
+            mHandler.post(() -> displayPolicy(view));
+        });
+    }
+
+    private void displayPolicy(PreferenceViewHolder view) {
+        if (mHasCredentialManagerPackage) {
+            RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
+            recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+
+            CredentialManagementAppAdapter recyclerViewAdapter = new CredentialManagementAppAdapter(
+                    mContext, mCredentialManagerPackageName,
+                    mCredentialManagerPolicy.getAppAndUriMappings(),
+                    /* include header= */ false, /* include expander= */ true);
+            recyclerView.setAdapter(recyclerViewAdapter);
+        }
+    }
+}
diff --git a/src/com/android/settings/security/CredentialManagementAppPreferenceController.java b/src/com/android/settings/security/CredentialManagementAppPreferenceController.java
new file mode 100644
index 0000000..107b8f2
--- /dev/null
+++ b/src/com/android/settings/security/CredentialManagementAppPreferenceController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.security;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Controller that shows and updates the credential management app summary.
+ */
+public class CredentialManagementAppPreferenceController extends BasePreferenceController {
+
+    private static final String TAG = "CredentialManagementApp";
+
+    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private final PackageManager mPackageManager;
+    private boolean mHasCredentialManagerPackage;
+    private String mCredentialManagerPackageName;
+
+    public CredentialManagementAppPreferenceController(Context context, String key) {
+        super(context, key);
+        mPackageManager = context.getPackageManager();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        mExecutor.execute(() -> {
+            try {
+                IKeyChainService service = KeyChain.bind(mContext).getService();
+                mHasCredentialManagerPackage = service.hasCredentialManagementApp();
+                mCredentialManagerPackageName = service.getCredentialManagementAppPackageName();
+            } catch (InterruptedException | RemoteException e) {
+                Log.e(TAG, "Unable to display credential management app preference");
+            }
+            mHandler.post(() -> displayPreference(preference));
+        });
+    }
+
+    private void displayPreference(Preference preference) {
+        if (mHasCredentialManagerPackage) {
+            preference.setEnabled(true);
+            try {
+                ApplicationInfo applicationInfo =
+                        mPackageManager.getApplicationInfo(mCredentialManagerPackageName, 0);
+                preference.setSummary(applicationInfo.loadLabel(mPackageManager));
+            } catch (PackageManager.NameNotFoundException e) {
+                preference.setSummary(mCredentialManagerPackageName);
+            }
+        } else {
+            preference.setEnabled(false);
+            preference.setSummary(R.string.no_certificate_management_app);
+        }
+    }
+}
diff --git a/src/com/android/settings/security/RequestManageCredentials.java b/src/com/android/settings/security/RequestManageCredentials.java
index 9d2d51e..b30f5b6 100644
--- a/src/com/android/settings/security/RequestManageCredentials.java
+++ b/src/com/android/settings/security/RequestManageCredentials.java
@@ -35,6 +35,8 @@
 
 import com.android.settings.R;
 
+import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
+
 /**
  * Displays a full screen to the user asking whether the calling app can manage the user's
  * KeyChain credentials. This screen includes the authentication policy highlighting what apps and
@@ -62,6 +64,7 @@
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
     private LinearLayout mButtonPanel;
+    private ExtendedFloatingActionButton mExtendedFab;
 
     private boolean mDisplayingButtonPanel = false;
 
@@ -79,6 +82,7 @@
 
             loadRecyclerView();
             loadButtons();
+            loadExtendedFloatingActionButton();
             addOnScrollListener();
         } else {
             Log.e(TAG, "Unable to start activity because intent action is not "
@@ -93,7 +97,8 @@
         mRecyclerView.setLayoutManager(mLayoutManager);
 
         CredentialManagementAppAdapter recyclerViewAdapter = new CredentialManagementAppAdapter(
-                this, mCredentialManagerPackage, mAuthenticationPolicy.getAppAndUriMappings());
+                this, mCredentialManagerPackage, mAuthenticationPolicy.getAppAndUriMappings(),
+                /* include header= */ true, /* include expander= */ false);
         mRecyclerView.setAdapter(recyclerViewAdapter);
     }
 
@@ -106,6 +111,15 @@
         allowButton.setOnClickListener(setCredentialManagementApp());
     }
 
+    private void loadExtendedFloatingActionButton() {
+        mExtendedFab = findViewById(R.id.extended_fab);
+        mExtendedFab.setOnClickListener(v -> {
+            mRecyclerView.scrollToPosition(mAuthenticationPolicy.getAppAndUriMappings().size());
+            mExtendedFab.hide();
+            showButtonPanel();
+        });
+    }
+
     private View.OnClickListener finishRequestManageCredentials() {
         return v -> {
             Toast.makeText(this, R.string.request_manage_credentials_dont_allow,
@@ -130,9 +144,16 @@
             public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                 super.onScrolled(recyclerView, dx, dy);
                 if (!mDisplayingButtonPanel) {
+                    // On down scroll, hide text in floating action button by setting
+                    // extended to false.
+                    if (dy > 0 && mExtendedFab.getVisibility() == View.VISIBLE) {
+                        mExtendedFab.setExtended(false);
+                    }
                     if (isRecyclerScrollable()) {
+                        mExtendedFab.show();
                         hideButtonPanel();
                     } else {
+                        mExtendedFab.hide();
                         showButtonPanel();
                     }
                 }
diff --git a/src/com/android/settings/sim/SimActivationNotifier.java b/src/com/android/settings/sim/SimActivationNotifier.java
new file mode 100644
index 0000000..85d3da2
--- /dev/null
+++ b/src/com/android/settings/sim/SimActivationNotifier.java
@@ -0,0 +1,144 @@
+/*
+ * 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.sim;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.annotation.IntDef;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.core.app.TaskStackBuilder;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.network.SubscriptionUtil;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class manages the notification of SIM activation notification including creating and
+ * canceling the notifications.
+ */
+public class SimActivationNotifier {
+
+    private static final String TAG = "SimActivationNotifier";
+    private static final String SIM_SETUP_CHANNEL_ID = "sim_setup";
+    private static final String SIM_PREFS = "sim_prefs";
+    private static final String KEY_SHOW_SIM_SETTINGS_NOTIFICATION =
+            "show_sim_settings_notification";
+
+    public static final int SIM_ACTIVATION_NOTIFICATION_ID = 1;
+
+    /** Notification types */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                NotificationType.NETWORK_CONFIG,
+            })
+    public @interface NotificationType {
+        // The notification to remind users to config network Settings.
+        int NETWORK_CONFIG = 1;
+    }
+
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+
+    public SimActivationNotifier(Context context) {
+        mContext = context;
+        mNotificationManager = context.getSystemService(NotificationManager.class);
+        mNotificationManager.createNotificationChannel(
+                new NotificationChannel(
+                        SIM_SETUP_CHANNEL_ID,
+                        mContext.getString(R.string.sim_setup_channel_id),
+                        NotificationManager.IMPORTANCE_HIGH));
+    }
+
+    /**
+     * Sets whether Settings should send a push notification for the SIM activation.
+     *
+     * @param context
+     * @param showNotification whether Settings should send a push notification for the SIM
+     *     activation.
+     */
+    public static void setShowSimSettingsNotification(Context context, boolean showNotification) {
+        final SharedPreferences prefs = context.getSharedPreferences(SIM_PREFS, MODE_PRIVATE);
+        prefs.edit().putBoolean(KEY_SHOW_SIM_SETTINGS_NOTIFICATION, showNotification).apply();
+    }
+
+    /**
+     * Gets whether Settings should send a push notification for the SIM activation.
+     *
+     * @param context
+     * @return true if Settings should send a push notification for SIM activation. Otherwise,
+     *     return false.
+     */
+    public static boolean getShowSimSettingsNotification(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(SIM_PREFS, MODE_PRIVATE);
+        return prefs.getBoolean(KEY_SHOW_SIM_SETTINGS_NOTIFICATION, false);
+    }
+
+    /** Sends a push notification for the SIM activation. It should be called after DSDS reboot. */
+    public void sendNetworkConfigNotification() {
+        SubscriptionManager subscriptionManager =
+                mContext.getSystemService(SubscriptionManager.class);
+        SubscriptionInfo activeRemovableSub =
+                SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream()
+                        .filter(sub -> !sub.isEmbedded())
+                        .findFirst()
+                        .orElse(null);
+
+        if (activeRemovableSub == null) {
+            Log.e(TAG, "No removable subscriptions found. Do not show notification.");
+            return;
+        }
+
+        String carrierName =
+                TextUtils.isEmpty(activeRemovableSub.getDisplayName())
+                        ? mContext.getString(R.string.sim_card_label)
+                        : activeRemovableSub.getDisplayName().toString();
+        String title =
+                mContext.getString(
+                        R.string.post_dsds_reboot_notification_title_with_carrier, carrierName);
+        String text = mContext.getString(R.string.post_dsds_reboot_notification_text);
+        Intent clickIntent = new Intent(mContext, Settings.MobileNetworkListActivity.class);
+        TaskStackBuilder stackBuilder =
+                TaskStackBuilder.create(mContext).addNextIntent(clickIntent);
+        PendingIntent contentIntent =
+                stackBuilder.getPendingIntent(
+                        0 /* requestCode */, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        Notification.Builder builder =
+                new Notification.Builder(mContext, SIM_SETUP_CHANNEL_ID)
+                        .setContentTitle(title)
+                        .setContentText(text)
+                        .setContentIntent(contentIntent)
+                        .setSmallIcon(R.drawable.ic_sim_alert)
+                        .setAutoCancel(true);
+        mNotificationManager.notify(SIM_ACTIVATION_NOTIFICATION_ID, builder.build());
+    }
+}
diff --git a/src/com/android/settings/sim/SimNotificationService.java b/src/com/android/settings/sim/SimNotificationService.java
new file mode 100644
index 0000000..303c21d
--- /dev/null
+++ b/src/com/android/settings/sim/SimNotificationService.java
@@ -0,0 +1,81 @@
+/*
+ * 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.sim;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import com.android.settings.R;
+
+/** A JobService sends SIM notifications. */
+public class SimNotificationService extends JobService {
+
+    private static final String TAG = "SimNotificationService";
+    private static final String EXTRA_NOTIFICATION_TYPE = "notification_type";
+
+    /**
+     * Schedules a service to send SIM push notifications.
+     * @param context
+     * @param notificationType indicates which SIM notification to send.
+     */
+    public static void scheduleSimNotification(
+            Context context, @SimActivationNotifier.NotificationType int notificationType) {
+        final JobScheduler jobScheduler =
+                context.getApplicationContext().getSystemService(JobScheduler.class);
+        final ComponentName component =
+                new ComponentName(context.getApplicationContext(), SimNotificationService.class);
+        PersistableBundle extra = new PersistableBundle();
+        extra.putInt(EXTRA_NOTIFICATION_TYPE, notificationType);
+
+        jobScheduler.schedule(
+                new JobInfo.Builder(R.integer.sim_notification_send, component)
+                        .setExtras(extra)
+                        .build());
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        PersistableBundle extra = params.getExtras();
+        if (extra == null) {
+            Log.e(TAG, "Failed to get notification type.");
+            return false;
+        }
+        int notificationType = extra.getInt(EXTRA_NOTIFICATION_TYPE);
+        switch (notificationType) {
+            case SimActivationNotifier.NotificationType.NETWORK_CONFIG:
+                Log.i(TAG, "Sending SIM config notification.");
+                SimActivationNotifier.setShowSimSettingsNotification(this, false);
+                new SimActivationNotifier(this).sendNetworkConfigNotification();
+                break;
+            default:
+                Log.e(TAG, "Invalid notification type: " + notificationType);
+                break;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/sim/receivers/SimCompleteBootReceiver.java b/src/com/android/settings/sim/receivers/SimCompleteBootReceiver.java
new file mode 100644
index 0000000..e9acf94
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SimCompleteBootReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * 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.sim.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.settings.sim.SimActivationNotifier;
+import com.android.settings.sim.SimNotificationService;
+
+/** This class manage all SIM operations after device boot up. */
+public class SimCompleteBootReceiver extends BroadcastReceiver {
+    private static final String TAG = "SimCompleteBootReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+            Log.e(TAG, "Invalid broadcast received.");
+            return;
+        }
+        if (SimActivationNotifier.getShowSimSettingsNotification(context)) {
+            SimNotificationService.scheduleSimNotification(
+                    context, SimActivationNotifier.NotificationType.NETWORK_CONFIG);
+        }
+    }
+}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
new file mode 100644
index 0000000..814f1a4
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
@@ -0,0 +1,229 @@
+/*
+ * 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.sim.receivers;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Looper;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotInfo;
+import android.util.Log;
+
+import com.android.settings.network.SubscriptionUtil;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+/** Perform actions after a slot change event is triggered. */
+public class SimSlotChangeHandler {
+    private static final String TAG = "SimSlotChangeHandler";
+
+    private static final String EUICC_PREFS = "euicc_prefs";
+    private static final String KEY_REMOVABLE_SLOT_STATE = "removable_slot_state";
+
+    private static volatile SimSlotChangeHandler sSlotChangeHandler;
+
+    /** Returns a SIM slot change handler singleton. */
+    public static SimSlotChangeHandler get() {
+        if (sSlotChangeHandler == null) {
+            synchronized (SimSlotChangeHandler.class) {
+                if (sSlotChangeHandler == null) {
+                    sSlotChangeHandler = new SimSlotChangeHandler();
+                }
+            }
+        }
+        return sSlotChangeHandler;
+    }
+
+    private SubscriptionManager mSubMgr;
+    private TelephonyManager mTelMgr;
+    private Context mContext;
+
+    void onSlotsStatusChange(Context context) {
+        init(context);
+
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            throw new IllegalStateException("Cannot be called from main thread.");
+        }
+
+        if (mTelMgr.getActiveModemCount() > 1) {
+            Log.i(TAG, "The device is already in DSDS mode. Do nothing.");
+            return;
+        }
+
+        UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
+        if (removableSlotInfo == null) {
+            Log.e(TAG, "Unable to find the removable slot. Do nothing.");
+            return;
+        }
+
+        int lastRemovableSlotState = getLastRemovableSimSlotState(mContext);
+        int currentRemovableSlotState = removableSlotInfo.getCardStateInfo();
+
+        // Sets the current removable slot state.
+        setRemovableSimSlotState(mContext, currentRemovableSlotState);
+
+        if (lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
+                && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT) {
+            handleSimInsert(removableSlotInfo);
+            return;
+        }
+        if (lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+                && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT) {
+            handleSimRemove(removableSlotInfo);
+            return;
+        }
+        Log.i(TAG, "Do nothing on slot status changes.");
+    }
+
+    private void init(Context context) {
+        mSubMgr =
+                (SubscriptionManager)
+                        context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mTelMgr = context.getSystemService(TelephonyManager.class);
+        mContext = context;
+    }
+
+    private void handleSimInsert(UiccSlotInfo removableSlotInfo) {
+        Log.i(TAG, "Detect SIM inserted.");
+
+        if (!isSuwFinished(mContext)) {
+            // TODO(b/170508680): Store the action and handle it after SUW is finished.
+            Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished");
+            return;
+        }
+
+        if (removableSlotInfo.getIsActive()) {
+            Log.i(TAG, "The removable slot is already active. Do nothing.");
+            return;
+        }
+
+        if (!hasActiveEsimSubscription()) {
+            if (mTelMgr.isMultiSimEnabled()) {
+                Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.");
+                // TODO(b/170508680): Display DSDS dialog to ask users whether to enable DSDS.
+            } else {
+                Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.");
+                // TODO(b/170508680): Display Choose a number to use screen for subscription
+                //  selection.
+            }
+            return;
+        }
+
+        Log.i(
+                TAG,
+                "No enabled eSIM profile. Ready to switch to removable slot and show"
+                        + " notification.");
+        // TODO(b/170508680): Switch the slot to the removebale slot and show the notification.
+    }
+
+    private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
+        Log.i(TAG, "Detect SIM removed.");
+
+        if (!isSuwFinished(mContext)) {
+            // TODO(b/170508680): Store the action and handle it after SUW is finished.
+            Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished");
+            return;
+        }
+
+        List<SubscriptionInfo> groupedEmbeddedSubscriptions = getGroupedEmbeddedSubscriptions();
+
+        if (groupedEmbeddedSubscriptions.size() == 0 || !removableSlotInfo.getIsActive()) {
+            Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing.");
+            return;
+        }
+
+        // If there is only 1 eSIM profile exists, we ask the user if they want to switch to that
+        // profile.
+        if (groupedEmbeddedSubscriptions.size() == 1) {
+            Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.");
+            // TODO(b/170508680): Display a dialog to ask users to switch.
+            return;
+        }
+
+        // If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
+        // the number they want to use.
+        Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.");
+        // TODO(b/170508680): Display a dialog to ask user which SIM to switch.
+    }
+
+    private int getLastRemovableSimSlotState(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
+        return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT);
+    }
+
+    private void setRemovableSimSlotState(Context context, int state) {
+        final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
+        prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply();
+    }
+
+    @Nullable
+    private UiccSlotInfo getRemovableUiccSlotInfo() {
+        UiccSlotInfo[] slotInfos = mTelMgr.getUiccSlotsInfo();
+        if (slotInfos == null) {
+            Log.e(TAG, "slotInfos is null. Unable to get slot infos.");
+            return null;
+        }
+        for (UiccSlotInfo slotInfo : slotInfos) {
+            if (slotInfo != null && slotInfo.isRemovable()) {
+
+                return slotInfo;
+            }
+        }
+        return null;
+    }
+
+    private static boolean isSuwFinished(Context context) {
+        try {
+            // DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
+            return Settings.Global.getInt(
+                            context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED)
+                    == 1;
+        } catch (Settings.SettingNotFoundException e) {
+            Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e);
+            return false;
+        }
+    }
+
+    private boolean hasActiveEsimSubscription() {
+        List<SubscriptionInfo> activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr);
+        return activeSubs.stream().anyMatch(SubscriptionInfo::isEmbedded);
+    }
+
+    private List<SubscriptionInfo> getGroupedEmbeddedSubscriptions() {
+        List<SubscriptionInfo> groupedSubscriptions =
+                SubscriptionUtil.getSelectableSubscriptionInfoList(mContext);
+        if (groupedSubscriptions == null) {
+            return ImmutableList.of();
+        }
+        return ImmutableList.copyOf(
+                groupedSubscriptions.stream()
+                        .filter(sub -> sub.isEmbedded())
+                        .collect(Collectors.toList()));
+    }
+
+    private SimSlotChangeHandler() {}
+}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
new file mode 100644
index 0000000..17a1b8d
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeReceiver.java
@@ -0,0 +1,139 @@
+/*
+ * 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.sim.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccCardInfo;
+import android.telephony.UiccSlotInfo;
+import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.List;
+
+/** The receiver when the slot status changes. */
+public class SimSlotChangeReceiver extends BroadcastReceiver {
+    private static final String TAG = "SlotChangeReceiver";
+
+    private final SimSlotChangeHandler mSlotChangeHandler = SimSlotChangeHandler.get();
+    private final Object mLock = new Object();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        String action = intent.getAction();
+        if (!TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED.equals(action)) {
+            Log.e(TAG, "Ignore slot changes due to unexpected action: " + action);
+            return;
+        }
+
+        ThreadUtils.postOnBackgroundThread(
+                () -> {
+                    synchronized (mLock) {
+                        if (!shouldHandleSlotChange(context)) {
+                            return;
+                        }
+                        mSlotChangeHandler.onSlotsStatusChange(context);
+                    }
+                });
+    }
+
+    // Checks whether the slot event should be handled.
+    private boolean shouldHandleSlotChange(Context context) {
+        final EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
+        if (euiccManager == null || !euiccManager.isEnabled()) {
+            Log.i(TAG, "Ignore slot changes because EuiccManager is disabled.");
+            return false;
+        }
+
+        if (euiccManager.getOtaStatus() == EuiccManager.EUICC_OTA_IN_PROGRESS) {
+            Log.i(TAG, "Ignore slot changes because eSIM OTA is in progress.");
+            return false;
+        }
+
+        if (!isSimSlotStateValid(context)) {
+            Log.i(TAG, "Ignore slot changes because SIM states are not valid.");
+            return false;
+        }
+
+        return true;
+    }
+
+    // Checks whether the SIM slot state is valid for slot change event.
+    private boolean isSimSlotStateValid(Context context) {
+        final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
+        UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
+        if (slotInfos == null) {
+            Log.e(TAG, "slotInfos is null. Unable to get slot infos.");
+            return false;
+        }
+
+        boolean isAllCardStringsEmpty = true;
+        for (int i = 0; i < slotInfos.length; i++) {
+            UiccSlotInfo slotInfo = slotInfos[i];
+
+            if (slotInfo == null) {
+                return false;
+            }
+
+            // After pSIM is inserted, there might be a short period that the status of both slots
+            // are not accurate. We drop the event if any of sim presence state is ERROR or
+            // RESTRICTED.
+            if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ERROR
+                    || slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
+                Log.i(TAG, "The SIM state is in an error. Drop the event. SIM info: " + slotInfo);
+                return false;
+            }
+
+            UiccCardInfo cardInfo = findUiccCardInfoBySlot(telMgr, i);
+            if (cardInfo == null) {
+                continue;
+            }
+            if (!TextUtils.isEmpty(slotInfo.getCardId())
+                    || !TextUtils.isEmpty(cardInfo.getIccId())) {
+                isAllCardStringsEmpty = false;
+            }
+        }
+
+        // We also drop the event if both the card strings are empty, which usually means it's
+        // between SIM slots switch the slot status is not stable at this moment.
+        if (isAllCardStringsEmpty) {
+            Log.i(TAG, "All UICC card strings are empty. Drop this event.");
+            return false;
+        }
+
+        return true;
+    }
+
+    @Nullable
+    private UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr, int physicalSlotIndex) {
+        List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo();
+        if (cardInfos == null) {
+            return null;
+        }
+        return cardInfos.stream()
+                .filter(info -> info.getSlotIndex() == physicalSlotIndex)
+                .findFirst()
+                .orElse(null);
+    }
+}
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index 3007953..ce98d27 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -41,6 +41,7 @@
 import com.android.settings.media.MediaOutputIndicatorSlice;
 import com.android.settings.media.MediaOutputSlice;
 import com.android.settings.media.RemoteMediaSlice;
+import com.android.settings.network.AirplaneSafeNetworksSlice;
 import com.android.settings.network.telephony.MobileDataSlice;
 import com.android.settings.notification.zen.ZenModeButtonPreferenceController;
 import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -314,6 +315,16 @@
             .appendPath("always_on_display")
             .build();
 
+    /**
+     * Backing Uri for the Always On Slice.
+     */
+    public static final Uri AIRPLANE_SAFE_NETWORKS_SLICE_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+            .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+            .appendPath("airplane_safe_networks")
+            .build();
+
     @VisibleForTesting
     static final Map<Uri, Class<? extends CustomSliceable>> sUriToSlice;
 
@@ -336,6 +347,7 @@
         sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);
         sUriToSlice.put(MEDIA_OUTPUT_GROUP_SLICE_URI, MediaOutputGroupSlice.class);
         sUriToSlice.put(ALWAYS_ON_SLICE_URI, AlwaysOnDisplaySlice.class);
+        sUriToSlice.put(AIRPLANE_SAFE_NETWORKS_SLICE_URI, AirplaneSafeNetworksSlice.class);
     }
 
     public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {
diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java
index 3245657..30e9967 100644
--- a/src/com/android/settings/tts/TextToSpeechSettings.java
+++ b/src/com/android/settings/tts/TextToSpeechSettings.java
@@ -318,11 +318,15 @@
         mDefaultRatePref.setProgress(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, mDefaultRate));
         mDefaultRatePref.setOnPreferenceChangeListener(this);
         mDefaultRatePref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, MAX_SPEECH_RATE));
+        mDefaultRatePref.setContinuousUpdates(true);
+        mDefaultRatePref.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
 
         mDefaultPitchPref.setProgress(
                 getSeekBarProgressFromValue(KEY_DEFAULT_PITCH, mDefaultPitch));
         mDefaultPitchPref.setOnPreferenceChangeListener(this);
         mDefaultPitchPref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_PITCH, MAX_SPEECH_PITCH));
+        mDefaultPitchPref.setContinuousUpdates(true);
+        mDefaultPitchPref.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
 
         if (mTts != null) {
             mCurrentEngine = mTts.getCurrentEngine();
diff --git a/src/com/android/settings/widget/GenericSwitchController.java b/src/com/android/settings/widget/GenericSwitchController.java
new file mode 100644
index 0000000..b1b4342
--- /dev/null
+++ b/src/com/android/settings/widget/GenericSwitchController.java
@@ -0,0 +1,114 @@
+/*
+ * 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.widget;
+
+import androidx.preference.Preference;
+
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+/**
+ * The switch controller that is used to update the switch widget in the PrimarySwitchPreference
+ * and RestrictedSwitchPreference layouts.
+ */
+public class GenericSwitchController extends SwitchWidgetController implements
+        Preference.OnPreferenceChangeListener {
+
+    private Preference mPreference;
+    private MetricsFeatureProvider mMetricsFeatureProvider;
+
+    public GenericSwitchController(PrimarySwitchPreference preference) {
+        setPreference(preference);
+    }
+
+    public GenericSwitchController(RestrictedSwitchPreference preference) {
+        setPreference(preference);
+    }
+
+    private void setPreference(Preference preference) {
+        mPreference = preference;
+        mMetricsFeatureProvider =
+                FeatureFactory.getFactory(preference.getContext()).getMetricsFeatureProvider();
+    }
+
+    @Override
+    public void updateTitle(boolean isChecked) {
+    }
+
+    @Override
+    public void startListening() {
+        mPreference.setOnPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void stopListening() {
+        mPreference.setOnPreferenceChangeListener(null);
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference).setChecked(checked);
+        } else if (mPreference instanceof RestrictedSwitchPreference) {
+            ((RestrictedSwitchPreference) mPreference).setChecked(checked);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        if (mPreference instanceof PrimarySwitchPreference) {
+            return ((PrimarySwitchPreference) mPreference).isChecked();
+        } else if (mPreference instanceof RestrictedSwitchPreference) {
+            return ((RestrictedSwitchPreference) mPreference).isChecked();
+        }
+        return false;
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference).setSwitchEnabled(enabled);
+        } else if (mPreference instanceof RestrictedSwitchPreference) {
+            ((RestrictedSwitchPreference) mPreference).setEnabled(enabled);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (mListener != null) {
+            final boolean result = mListener.onSwitchToggled((Boolean) newValue);
+            if (result) {
+                mMetricsFeatureProvider.logClickedPreference(preference,
+                        preference.getExtras().getInt(DashboardFragment.CATEGORY));
+            }
+            return result;
+        }
+        return false;
+    }
+
+    @Override
+    public void setDisabledByAdmin(EnforcedAdmin admin) {
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference).setDisabledByAdmin(admin);
+        } else if (mPreference instanceof RestrictedSwitchPreference) {
+            ((RestrictedSwitchPreference) mPreference).setDisabledByAdmin(admin);
+        }
+    }
+}
diff --git a/src/com/android/settings/widget/LabeledSeekBar.java b/src/com/android/settings/widget/LabeledSeekBar.java
index 14c3d7d..5945c75 100644
--- a/src/com/android/settings/widget/LabeledSeekBar.java
+++ b/src/com/android/settings/widget/LabeledSeekBar.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.widget;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -48,6 +50,8 @@
     /** Labels for discrete progress values. */
     private String[] mLabels;
 
+    private int mLastProgress = -1;
+
     public LabeledSeekBar(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.seekBarStyle);
     }
@@ -118,6 +122,10 @@
                 mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
                 sendClickEventForAccessibility(progress);
             }
+            if (progress != mLastProgress) {
+                seekBar.performHapticFeedback(CLOCK_TICK);
+                mLastProgress = progress;
+            }
         }
     };
 
diff --git a/src/com/android/settings/widget/PrimarySwitchController.java b/src/com/android/settings/widget/PrimarySwitchController.java
deleted file mode 100644
index 3718a89..0000000
--- a/src/com/android/settings/widget/PrimarySwitchController.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 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.widget;
-
-import androidx.preference.Preference;
-
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-/**
- * The switch controller that is used to update the switch widget in the PrimarySwitchPreference
- * layout.
- */
-public class PrimarySwitchController extends SwitchWidgetController implements
-        Preference.OnPreferenceChangeListener {
-
-    private final PrimarySwitchPreference mPreference;
-    private final MetricsFeatureProvider mMetricsFeatureProvider;
-
-    public PrimarySwitchController(PrimarySwitchPreference preference) {
-        mPreference = preference;
-        mMetricsFeatureProvider = FeatureFactory.getFactory(preference.getContext())
-                .getMetricsFeatureProvider();
-    }
-
-    @Override
-    public void updateTitle(boolean isChecked) {
-    }
-
-    @Override
-    public void startListening() {
-        mPreference.setOnPreferenceChangeListener(this);
-    }
-
-    @Override
-    public void stopListening() {
-        mPreference.setOnPreferenceChangeListener(null);
-    }
-
-    @Override
-    public void setChecked(boolean checked) {
-        mPreference.setChecked(checked);
-    }
-
-    @Override
-    public boolean isChecked() {
-        return mPreference.isChecked();
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        mPreference.setSwitchEnabled(enabled);
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        if (mListener != null) {
-            final boolean result = mListener.onSwitchToggled((Boolean) newValue);
-            if (result) {
-                mMetricsFeatureProvider.logClickedPreference(preference,
-                        preference.getExtras().getInt(DashboardFragment.CATEGORY));
-            }
-            return result;
-        }
-        return false;
-    }
-
-    @Override
-    public void setDisabledByAdmin(EnforcedAdmin admin) {
-        mPreference.setDisabledByAdmin(admin);
-    }
-}
diff --git a/src/com/android/settings/wifi/WifiPrimarySwitchPreferenceController.java b/src/com/android/settings/wifi/WifiPrimarySwitchPreferenceController.java
index b2bd67f..fdc12aa 100644
--- a/src/com/android/settings/wifi/WifiPrimarySwitchPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiPrimarySwitchPreferenceController.java
@@ -21,7 +21,7 @@
 
 import com.android.settings.R;
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.widget.PrimarySwitchController;
+import com.android.settings.widget.GenericSwitchController;
 import com.android.settings.widget.PrimarySwitchPreference;
 import com.android.settings.widget.SummaryUpdater;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -89,7 +89,7 @@
 
     @Override
     public void onStart() {
-        mWifiEnabler = new WifiEnabler(mContext, new PrimarySwitchController(mWifiPreference),
+        mWifiEnabler = new WifiEnabler(mContext, new GenericSwitchController(mWifiPreference),
                 mMetricsFeatureProvider);
     }
 
diff --git a/tests/robotests/res/xml-mcc999/about_legal.xml b/tests/robotests/res/xml-mcc999/about_legal.xml
index 3e008cb..cbbc99a 100644
--- a/tests/robotests/res/xml-mcc999/about_legal.xml
+++ b/tests/robotests/res/xml-mcc999/about_legal.xml
@@ -24,7 +24,7 @@
 
 <PreferenceScreen
         xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+        xmlns:settings="http://schemas.android.com/apk/res-auto"
         android:title="bears_bears_bears"
         settings:keywords="keywords">
 
diff --git a/tests/robotests/res/xml-mcc999/display_settings.xml b/tests/robotests/res/xml-mcc999/display_settings.xml
index fccad7f..b5bf789 100644
--- a/tests/robotests/res/xml-mcc999/display_settings.xml
+++ b/tests/robotests/res/xml-mcc999/display_settings.xml
@@ -23,7 +23,7 @@
 
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:title="page_title"
     settings:keywords="keywords">
 
diff --git a/tests/robotests/src/com/android/settings/PointerSpeedPreferenceTest.java b/tests/robotests/src/com/android/settings/PointerSpeedPreferenceTest.java
new file mode 100644
index 0000000..0925344
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/PointerSpeedPreferenceTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class PointerSpeedPreferenceTest {
+
+    private Context mContext;
+    private AttributeSet mAttrs;
+    private SeekBar mSeekBar;
+    private PointerSpeedPreference mPointerSpeedPreference;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mSeekBar = new SeekBar(mContext, mAttrs);
+        mPointerSpeedPreference = new PointerSpeedPreference(mContext, mAttrs);
+    }
+
+    @Test
+    public void onProgressChanged_minimumValue_clockTickFeedbackPerformed() {
+        mSeekBar.performHapticFeedback(CONTEXT_CLICK);
+        mPointerSpeedPreference.onProgressChanged(mSeekBar, 0, true);
+
+        assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java b/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
index e0dc681..93f1a7b 100644
--- a/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/BalanceSeekBarTest.java
@@ -16,12 +16,16 @@
 
 package com.android.settings.accessibility;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -49,7 +53,7 @@
     public void setUp() {
         mContext = RuntimeEnvironment.application;
         mSeekBar = new BalanceSeekBar(mContext, mAttrs);
-        mProxySeekBarListener = mSeekBar.getProxySeekBarListener();
+        mProxySeekBarListener = shadowOf(mSeekBar).getOnSeekBarChangeListener();
         mockSeekBarChangeListener = mock(SeekBar.OnSeekBarChangeListener.class);
         mSeekBar.setOnSeekBarChangeListener(mockSeekBarChangeListener);
     }
@@ -78,6 +82,31 @@
     }
 
     @Test
+    public void onProgressChanged_minimumValue_clockTickFeedbackPerformed() {
+        mSeekBar.performHapticFeedback(CONTEXT_CLICK);
+        mProxySeekBarListener.onProgressChanged(mSeekBar, 0, true);
+
+        assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
+    }
+
+    @Test
+    public void onProgressChanged_centerValue_clockTickFeedbackPerformed() {
+        mSeekBar.performHapticFeedback(CONTEXT_CLICK);
+        mProxySeekBarListener.onProgressChanged(mSeekBar, MAX_PROGRESS_VALUE / 2, true);
+
+        assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
+    }
+
+    @Test
+    public void onProgressChanged_maximumValue_clockTickFeedbackPerformed() {
+        mSeekBar.setMax(MAX_PROGRESS_VALUE);
+        mSeekBar.performHapticFeedback(CONTEXT_CLICK);
+        mProxySeekBarListener.onProgressChanged(mSeekBar, MAX_PROGRESS_VALUE, true);
+
+        assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
+    }
+
+    @Test
     public void setMaxTest_shouldSetValue() {
         mSeekBar.setMax(MAX_PROGRESS_VALUE);
 
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
index fb565b6..afda8d5 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
@@ -73,7 +73,7 @@
         mLifecycle.handleLifecycleEvent(ON_START);
 
         mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), null);
 
         verify(mListener).onZenAccessPolicyChanged();
     }
@@ -84,7 +84,7 @@
         mLifecycle.handleLifecycleEvent(ON_STOP);
 
         mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), null);
 
         verify(mListener, never()).onZenAccessPolicyChanged();
     }
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
index 7313321..6401388 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
@@ -41,7 +41,7 @@
 import org.robolectric.RuntimeEnvironment;
 
 @RunWith(RobolectricTestRunner.class)
-public class BluetoothDetailsControllerTestBase {
+public abstract class BluetoothDetailsControllerTestBase {
 
     protected Context mContext;
     private LifecycleOwner mLifecycleOwner;
diff --git a/tests/robotests/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceControllerTest.java
new file mode 100644
index 0000000..06e66a1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/transcode/TranscodeDefaultOptionPreferenceControllerTest.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.development.transcode;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class TranscodeDefaultOptionPreferenceControllerTest {
+    private static final String TRANSCODE_DEFAULT_SYS_PROP_KEY =
+            "persist.sys.fuse.transcode_default";
+
+    private TranscodeDefaultOptionPreferenceController mUnderTest;
+
+    @Before
+    public void setUp() {
+        Context context = ApplicationProvider.getApplicationContext();
+        mUnderTest = new TranscodeDefaultOptionPreferenceController(context, "some_key");
+    }
+
+    @Test
+    public void isChecked_whenSysPropSet_shouldReturnFalse() {
+        SystemProperties.set(TRANSCODE_DEFAULT_SYS_PROP_KEY, "true");
+        assertThat(mUnderTest.isChecked()).isFalse();
+    }
+
+    @Test
+    public void isChecked_whenSysPropUnset_shouldReturnTrue() {
+        SystemProperties.set(TRANSCODE_DEFAULT_SYS_PROP_KEY, "false");
+        assertThat(mUnderTest.isChecked()).isTrue();
+    }
+
+    @Test
+    public void setChecked_withTrue_shouldUnsetSysProp() {
+        mUnderTest.setChecked(true);
+        assertThat(
+                SystemProperties.getBoolean(TRANSCODE_DEFAULT_SYS_PROP_KEY, true)).isFalse();
+    }
+
+    @Test
+    public void setChecked_withFalse_shouldSetSysProp() {
+        mUnderTest.setChecked(false);
+        assertThat(
+                SystemProperties.getBoolean(TRANSCODE_DEFAULT_SYS_PROP_KEY, false)).isTrue();
+    }
+
+    @Test
+    public void getAvailabilityStatus_shouldReturn_isAvailable() {
+        assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceControllerTest.java
index b22bf9d..aa2e672 100644
--- a/tests/robotests/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/transcode/TranscodeGlobalTogglePreferenceControllerTest.java
@@ -31,7 +31,7 @@
 @RunWith(RobolectricTestRunner.class)
 public class TranscodeGlobalTogglePreferenceControllerTest {
 
-    private static final String TRANSCODE_ENABLED_PROP_KEY = "persist.sys.fuse.transcode";
+    private static final String TRANSCODE_ENABLED_PROP_KEY = "persist.sys.fuse.transcode_enabled";
 
     private TranscodeGlobalTogglePreferenceController mController;
 
@@ -47,15 +47,15 @@
     }
 
     @Test
-    public void isChecked_whenDisabled_shouldReturnTrue() {
+    public void isChecked_whenDisabled_shouldReturnFalse() {
         SystemProperties.set(TRANSCODE_ENABLED_PROP_KEY, "false");
-        assertThat(mController.isChecked()).isTrue();
+        assertThat(mController.isChecked()).isFalse();
     }
 
     @Test
-    public void isChecked_whenEnabled_shouldReturnFalse() {
+    public void isChecked_whenEnabled_shouldReturnTrue() {
         SystemProperties.set(TRANSCODE_ENABLED_PROP_KEY, "true");
-        assertThat(mController.isChecked()).isFalse();
+        assertThat(mController.isChecked()).isTrue();
     }
 
     @Test
@@ -64,7 +64,7 @@
         mController.setChecked(true);
 
         // Verify the system property was updated.
-        assertThat(SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, true)).isFalse();
+        assertThat(SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, false)).isTrue();
     }
 
     @Test
@@ -73,6 +73,6 @@
         mController.setChecked(false);
 
         // Verify the system property was updated.
-        assertThat(SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, false)).isTrue();
+        assertThat(SystemProperties.getBoolean(TRANSCODE_ENABLED_PROP_KEY, true)).isFalse();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceControllerTest.java
new file mode 100644
index 0000000..8cba0c3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/transcode/TranscodeUserControlPreferenceControllerTest.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.development.transcode;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class TranscodeUserControlPreferenceControllerTest {
+    private static final String TRANSCODE_USER_CONTROL_SYS_PROP_KEY =
+            "persist.sys.fuse.transcode_user_control";
+
+    private TranscodeUserControlPreferenceController mUnderTest;
+
+    @Before
+    public void setUp() {
+        Context context = ApplicationProvider.getApplicationContext();
+        mUnderTest = new TranscodeUserControlPreferenceController(context, "some_key");
+    }
+
+    @Test
+    public void isChecked_whenSysPropSet_shouldReturnTrue() {
+        SystemProperties.set(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, "true");
+        assertThat(mUnderTest.isChecked()).isTrue();
+    }
+
+    @Test
+    public void isChecked_whenSysPropUnset_shouldReturnFalse() {
+        SystemProperties.set(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, "false");
+        assertThat(mUnderTest.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setChecked_withTrue_shouldSetSysProp() {
+        mUnderTest.setChecked(true);
+        assertThat(
+                SystemProperties.getBoolean(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, false)).isTrue();
+    }
+
+    @Test
+    public void setChecked_withFalse_shouldUnsetSysProp() {
+        mUnderTest.setChecked(false);
+        assertThat(
+                SystemProperties.getBoolean(TRANSCODE_USER_CONTROL_SYS_PROP_KEY, true)).isFalse();
+    }
+
+    @Test
+    public void getAvailabilityStatus_shouldReturn_isAvailable() {
+        assertThat(mUnderTest.getAvailabilityStatus()).isEqualTo(
+                BasePreferenceController.AVAILABLE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImplTest.java
index b6b594c..c28267b 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyFeatureProviderImplTest.java
@@ -63,6 +63,7 @@
 import java.util.Date;
 import java.util.List;
 
+
 @RunWith(RobolectricTestRunner.class)
 public class EnterprisePrivacyFeatureProviderImplTest {
 
@@ -426,6 +427,17 @@
         verify(mContext).startActivity(intentEquals(intent));
     }
 
+    @Test
+    public void testShowParentalControls() {
+        when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(any()))
+                .thenReturn(mOwner);
+
+        // If the intent is resolved, then we can use it to launch the activity
+        Intent intent = addParentalControlsIntent(mOwner.getPackageName());
+        assertThat(mProvider.showParentalControls()).isTrue();
+        verify(mContext).startActivity(intentEquals(intent));
+    }
+
     private Intent addWorkPolicyInfoIntent(
             String packageName, boolean deviceOwner, boolean profileOwner) {
         Intent intent = new Intent(Settings.ACTION_SHOW_WORK_POLICY_INFO);
@@ -450,6 +462,23 @@
         return intent;
     }
 
+    private Intent addParentalControlsIntent(String packageName) {
+        Intent intent = new Intent(EnterprisePrivacyFeatureProviderImpl.ACTION_PARENTAL_CONTROLS);
+        intent.setPackage(packageName);
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.resolvePackageName = packageName;
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.name = "activityName";
+        resolveInfo.activityInfo.packageName = packageName;
+
+        List<ResolveInfo> activities = ImmutableList.of(resolveInfo);
+        when(mPackageManager.queryIntentActivities(intentEquals(intent), anyInt()))
+                .thenReturn(activities);
+        when(mPackageManager.queryIntentActivitiesAsUser(intentEquals(intent), anyInt(), anyInt()))
+                .thenReturn(activities);
+        return intent;
+    }
+
     private static class IntentMatcher implements ArgumentMatcher<Intent> {
         private final Intent mExpectedIntent;
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index 742daf2..a072988 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -66,6 +66,7 @@
         mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(mContext);
         mBatteryBroadcastReceiver.mBatteryLevel = BATTERY_INIT_LEVEL;
         mBatteryBroadcastReceiver.mBatteryStatus = BATTERY_INIT_STATUS;
+        mBatteryBroadcastReceiver.mBatteryHealth = BatteryManager.BATTERY_HEALTH_UNKNOWN;
         mBatteryBroadcastReceiver.setBatteryChangedListener(mBatteryListener);
 
         mChargingIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
@@ -95,6 +96,21 @@
             BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
             BatteryFixSliceTest.ShadowBatteryTipLoader.class
     })
+    public void testOnReceive_batteryHealthChanged_dataUpdated() {
+        mChargingIntent
+                .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT);
+        mBatteryBroadcastReceiver.onReceive(mContext, mChargingIntent);
+
+        assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
+                .isEqualTo(BatteryManager.BATTERY_HEALTH_OVERHEAT);
+        verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_HEALTH);
+    }
+
+    @Test
+    @Config(shadows = {
+            BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+            BatteryFixSliceTest.ShadowBatteryTipLoader.class
+    })
     public void onReceive_batteryNotPresent_shouldShowHelpMessage() {
         mChargingIntent.putExtra(BatteryManager.EXTRA_PRESENT, false);
 
@@ -131,6 +147,8 @@
 
         assertThat(mBatteryBroadcastReceiver.mBatteryLevel).isEqualTo(batteryLevel);
         assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(batteryStatus);
+        assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
+                .isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN);
         verify(mBatteryListener, never()).onBatteryChanged(anyInt());
     }
 
@@ -149,6 +167,8 @@
                 .isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
         assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
                 .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+        assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
+                .isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN);
         // 2 times because register will force update the battery
         verify(mBatteryListener, times(2)).onBatteryChanged(BatteryUpdateType.MANUAL);
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index a6cf653..ac3c8f9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -176,6 +176,15 @@
     }
 
     @Test
+    public void updatePreference_isOverheat_showEmptyText() {
+        mBatteryInfo.isOverheated = true;
+
+        mController.updateHeaderPreference(mBatteryInfo);
+
+        assertThat(mSummary.getText().toString().isEmpty()).isTrue();
+    }
+
+    @Test
     public void onStart_shouldStyleActionBar() {
         when(mEntityHeaderController.setRecyclerView(nullable(RecyclerView.class), eq(mLifecycle)))
                 .thenReturn(mEntityHeaderController);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index e88295c..c81e81c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -246,6 +246,22 @@
         assertThat(info.chargeLabel).isEqualTo("100%");
     }
 
+    @Test
+    public void testGetBatteryInfo_chargingWithOverheated_updateChargeLabel() {
+        doReturn(TEST_CHARGE_TIME_REMAINING)
+                .when(mBatteryStats)
+                .computeChargeTimeRemaining(anyLong());
+        mChargingBatteryBroadcast
+                .putExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_OVERHEAT);
+
+        BatteryInfo info = BatteryInfo.getBatteryInfo(mContext, mChargingBatteryBroadcast,
+                mBatteryStats, MOCK_ESTIMATE, SystemClock.elapsedRealtime() * 1000,
+                false /* shortString */);
+
+        assertThat(info.isOverheated).isTrue();
+        assertThat(info.chargeLabel).isEqualTo("50% - Battery limited temporarily");
+    }
+
     // Make our battery stats return a sequence of battery events.
     private void mockBatteryStatsHistory() {
         // Mock out new data every time start...Locked is called.
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 89f51a7..f393da8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -144,6 +144,8 @@
     @Mock
     private BatterySipper mIdleBatterySipper;
     @Mock
+    private BatteryInfo mBatteryInfo;
+    @Mock
     private Bundle mBundle;
     @Mock
     private UserManager mUserManager;
@@ -754,4 +756,36 @@
         assertThat(estimate.isBasedOnUsage()).isTrue();
         assertThat(estimate.getAverageDischargeTime()).isEqualTo(1000);
     }
+
+    @Test
+    public void testIsBatteryDefenderOn_isOverheatedAndIsCharging_returnTrue() {
+        mBatteryInfo.isOverheated = true;
+        mBatteryInfo.discharging = false;
+
+        assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isTrue();
+    }
+
+    @Test
+    public void testIsBatteryDefenderOn_isOverheatedAndDischarging_returnFalse() {
+        mBatteryInfo.isOverheated = true;
+        mBatteryInfo.discharging = true;
+
+        assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse();
+    }
+
+    @Test
+    public void testIsBatteryDefenderOn_notOverheatedAndDischarging_returnFalse() {
+        mBatteryInfo.isOverheated = false;
+        mBatteryInfo.discharging = true;
+
+        assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse();
+    }
+
+    @Test
+    public void testIsBatteryDefenderOn_notOverheatedAndIsCharging_returnFalse() {
+        mBatteryInfo.isOverheated = false;
+        mBatteryInfo.discharging = false;
+
+        assertThat(mBatteryUtils.isBatteryDefenderOn(mBatteryInfo)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index 116033b..8cc17d8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -50,6 +50,7 @@
 public class BatteryTipLoaderTest {
 
     private static final int[] TIP_ORDER = {
+            BatteryTip.TipType.BATTERY_DEFENDER,
             BatteryTip.TipType.BATTERY_SAVER,
             BatteryTip.TipType.HIGH_DEVICE_USAGE,
             BatteryTip.TipType.LOW_BATTERY,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
index 275bfe0..6199788 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
@@ -23,10 +23,12 @@
 
 import com.android.settings.SettingsActivity;
 import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.fuelgauge.batterytip.actions.BatteryDefenderAction;
 import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
 import com.android.settings.fuelgauge.batterytip.actions.OpenBatterySaverAction;
 import com.android.settings.fuelgauge.batterytip.actions.OpenRestrictAppFragmentAction;
 import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryDefenderTip;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.EarlyWarningTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
@@ -53,6 +55,7 @@
     private RestrictAppTip mRestrictAppTip;
     private EarlyWarningTip mEarlyWarningTip;
     private LowBatteryTip mLowBatteryTip;
+    private BatteryDefenderTip mBatteryDefenderTip;
 
     @Before
     public void setUp() {
@@ -67,6 +70,7 @@
         mLowBatteryTip = spy(
                 new LowBatteryTip(BatteryTip.StateType.NEW, false /* powerSaveModeOn */,
                         "" /* summary */));
+        mBatteryDefenderTip = spy(new BatteryDefenderTip(BatteryTip.StateType.NEW));
     }
 
     @Test
@@ -116,4 +120,13 @@
         assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
                 mFragment)).isInstanceOf(OpenBatterySaverAction.class);
     }
+
+    @Test
+    public void
+            testGetActionForBatteryTip_typeBatteryDefenderStateNew_returnActionBatteryDefender() {
+        when(mBatteryDefenderTip.getState()).thenReturn(BatteryTip.StateType.NEW);
+
+        assertThat(BatteryTipUtils.getActionForBatteryTip(mBatteryDefenderTip, mSettingsActivity,
+                mFragment)).isInstanceOf(BatteryDefenderAction.class);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java
new file mode 100644
index 0000000..a1f9d1f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryDefenderDetectorTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batterytip.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryDefenderDetectorTest {
+
+    @Mock
+    private BatteryInfo mBatteryInfo;
+    private BatteryDefenderDetector mBatteryDefenderDetector;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mBatteryInfo.discharging = false;
+
+        mBatteryDefenderDetector = new BatteryDefenderDetector(mBatteryInfo);
+    }
+
+    @Test
+    public void testDetect_notOverheated_tipInvisible() {
+        mBatteryInfo.isOverheated = false;
+
+        assertThat(mBatteryDefenderDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_isOverheated_tipNew() {
+        mBatteryInfo.isOverheated = true;
+
+        assertThat(mBatteryDefenderDetector.detect().getState())
+                .isEqualTo(BatteryTip.StateType.NEW);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.java
new file mode 100644
index 0000000..c6eb15d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTipTest.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.fuelgauge.batterytip.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryDefenderTipTest {
+
+    private Context mContext;
+    private BatteryDefenderTip mBatteryDefenderTip;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mBatteryDefenderTip = new BatteryDefenderTip(BatteryTip.StateType.NEW);
+    }
+
+    @Test
+    public void getTitle_showTitle() {
+        assertThat(mBatteryDefenderTip.getTitle(mContext))
+                .isEqualTo(mContext.getString(R.string.battery_tip_limited_temporarily_title));
+    }
+
+    @Test
+    public void getSummary_showSummary() {
+        assertThat(mBatteryDefenderTip.getSummary(mContext))
+                .isEqualTo(mContext.getString(R.string.battery_tip_limited_temporarily_summary));
+    }
+
+    @Test
+    public void getIcon_showIcon() {
+        assertThat(mBatteryDefenderTip.getIconId())
+                .isEqualTo(R.drawable.ic_battery_status_good_24dp);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
index 036df2c..6097980 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
@@ -21,6 +21,7 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 
 import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment.KEY_LOCK_SETTINGS_FOOTER;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME;
@@ -90,6 +91,7 @@
         Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
         ShadowStorageManager.reset();
         ShadowPersistentDataBlockManager.reset();
+        ShadowLockPatternUtils.reset();
     }
 
     @Test
@@ -377,6 +379,64 @@
                 ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL)).isNotNull();
     }
 
+    @Test
+    public void updatePreferencesOrFinish_ComplexityIsReadFromDPM() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
+
+        initActivity(null);
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+
+        FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
+        assertThat(footer.getTitle()).isEqualTo(null);
+
+        Intent intent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
+        assertThat(intent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
+                PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+    }
+
+    @Test
+    public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_LOW);
+        initActivity(intent);
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+
+        // Footer should be null because admin complexity wins.
+        FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
+        assertThat(footer.getTitle()).isEqualTo(null);
+
+        Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
+        assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
+                PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+    }
+
+    @Test
+    public void updatePreferencesOrFinish_ComplexityIsMergedWithDPM_AppIsHigher() {
+        ShadowStorageManager.setIsFileEncryptedNativeOrEmulated(false);
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
+        Intent intent = new Intent()
+                .putExtra(EXTRA_KEY_CALLER_APP_NAME, "app name")
+                .putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, PASSWORD_COMPLEXITY_HIGH);
+        initActivity(intent);
+
+        mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */);
+
+        // Footer should include app name because app requirement is higher.
+        CharSequence expectedTitle =
+                mActivity.getString(R.string.unlock_footer_high_complexity_requested, "app name");
+        FooterPreference footer = mFragment.findPreference(KEY_LOCK_SETTINGS_FOOTER);
+        assertThat(footer.getTitle()).isEqualTo(expectedTitle);
+
+        Intent passwordIntent = mFragment.getLockPasswordIntent(PASSWORD_QUALITY_COMPLEX);
+        assertThat(passwordIntent.getIntExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY,
+                PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
+    }
+
     private void initActivity(@Nullable Intent intent) {
         if (intent == null) {
             intent = new Intent();
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
index 030bb80..0ca6a3e 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java
@@ -31,6 +31,7 @@
 import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY;
 import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS;
 import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY;
+import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -67,6 +68,7 @@
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {
         SettingsShadowResources.class,
+        ShadowLockPatternUtils.class,
         ShadowUtils.class,
         ShadowDevicePolicyManager.class,
 })
@@ -84,6 +86,7 @@
     @After
     public void tearDown() {
         SettingsShadowResources.reset();
+        ShadowLockPatternUtils.reset();
     }
 
     @Test
@@ -378,6 +381,29 @@
         assertThat(drawable.getCreatedFromResId()).isNotEqualTo(R.drawable.ic_fingerprint_header);
     }
 
+    @Test
+    public void validateComplexityMergedFromDpmOnCreate() {
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
+
+        assertPasswordValidationResult(
+                /* minComplexity= */ PASSWORD_COMPLEXITY_HIGH,
+                /* passwordType= */ PASSWORD_QUALITY_NUMERIC,
+                /* userEnteredPassword= */ LockscreenCredential.createNone(),
+                "PIN must be at least 8 digits");
+    }
+
+    @Test
+    public void validateComplexityMergedFromUnificationUserOnCreate() {
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW);
+        ShadowLockPatternUtils.setRequiredPasswordComplexity(123, PASSWORD_COMPLEXITY_HIGH);
+
+        Intent intent = createIntentForPasswordValidation(PASSWORD_COMPLEXITY_NONE,
+                PASSWORD_QUALITY_NUMERIC);
+        intent.putExtra(EXTRA_KEY_UNIFICATION_PROFILE_ID, 123);
+        assertPasswordValidationResultForIntent(LockscreenCredential.createNone(), intent,
+                "PIN must be at least 8 digits");
+    }
+
     private ChooseLockPassword buildChooseLockPasswordActivity(Intent intent) {
         return Robolectric.buildActivity(ChooseLockPassword.class, intent).setup().get();
     }
@@ -400,14 +426,27 @@
     private void assertPasswordValidationResult(@PasswordComplexity int minComplexity,
             int passwordType, LockscreenCredential userEnteredPassword,
             String... expectedValidationResult) {
-        Intent intent = new Intent();
-        intent.putExtra(CONFIRM_CREDENTIALS, false);
-        intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
-        intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
+        Intent intent = createIntentForPasswordValidation(minComplexity, passwordType);
+        assertPasswordValidationResultForIntent(userEnteredPassword, intent,
+                expectedValidationResult);
+    }
+
+    private void assertPasswordValidationResultForIntent(LockscreenCredential userEnteredPassword,
+            Intent intent, String... expectedValidationResult) {
         ChooseLockPassword activity = buildChooseLockPasswordActivity(intent);
         ChooseLockPasswordFragment fragment = getChooseLockPasswordFragment(activity);
         fragment.validatePassword(userEnteredPassword);
         String[] messages = fragment.convertErrorCodeToMessages();
-        assertThat(messages).asList().containsExactly((Object[]) expectedValidationResult);
+        assertThat(messages).asList().containsExactly(expectedValidationResult);
+    }
+
+    private Intent createIntentForPasswordValidation(
+            @PasswordComplexity int minComplexity,
+            int passwordType) {
+        Intent intent = new Intent();
+        intent.putExtra(CONFIRM_CREDENTIALS, false);
+        intent.putExtra(PASSWORD_TYPE_KEY, passwordType);
+        intent.putExtra(EXTRA_KEY_REQUESTED_MIN_COMPLEXITY, minComplexity);
+        return intent;
     }
 }
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java
new file mode 100644
index 0000000..ecc2f2d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppButtonsControllerTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppButtonsControllerTest {
+
+    private Context mContext;
+    private CredentialManagementAppButtonsController mController;
+
+    private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mController = new CredentialManagementAppButtonsController(
+                mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+    }
+
+    @Test
+    public void getAvailabilityStatus_shouldAlwaysReturnAvailableUnsearchable() {
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java
new file mode 100644
index 0000000..1e6f203
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppControllerTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppControllerTest {
+
+    private Context mContext;
+    private CredentialManagementAppPreferenceController mController;
+    private Preference mPreference;
+
+    private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mController = new CredentialManagementAppPreferenceController(
+                mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+        mPreference = new Preference(mContext);
+    }
+
+    @Test
+    public void getAvailabilityStatus_shouldAlwaysReturnAvailable() {
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE);
+    }
+
+    @Test
+    public void updateState_noCredentialManagementApp_shouldDisablePreference() {
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isEnabled()).isEqualTo(false);
+        assertThat(mPreference.getSummary()).isEqualTo(
+                mContext.getText(R.string.no_certificate_management_app));
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.java
new file mode 100644
index 0000000..de19e5c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppFragmentTest.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.security;
+
+import static android.app.settings.SettingsEnums.CREDENTIAL_MANAGEMENT_APP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDashboardFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowDashboardFragment.class)
+public class CredentialManagementAppFragmentTest {
+
+    private CredentialManagementAppFragment mFragment;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mFragment = spy(new CredentialManagementAppFragment());
+
+        doReturn(mContext).when(mFragment).getContext();
+    }
+
+    @Test
+    public void searchIndexProvider_shouldIndexResource() {
+        final List<SearchIndexableResource> indexRes =
+                CredentialManagementAppFragment.SEARCH_INDEX_DATA_PROVIDER
+                        .getXmlResourcesToIndex(mContext, true /* enabled */);
+
+        assertThat(indexRes).isNotNull();
+        assertThat(indexRes.get(0).xmlResId).isEqualTo(R.xml.credential_management_app_fragment);
+    }
+
+    @Test
+    public void getMetricsCategory_shouldReturnInstallCertificateFromStorage() {
+        assertThat(mFragment.getMetricsCategory()).isEqualTo(CREDENTIAL_MANAGEMENT_APP);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java b/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java
new file mode 100644
index 0000000..e77e4c1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/security/CredentialManagementAppHeaderControllerTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class CredentialManagementAppHeaderControllerTest {
+
+    private Context mContext;
+    private CredentialManagementAppHeaderController mController;
+
+    private static final String PREF_KEY_CREDENTIAL_MANAGEMENT_APP = "certificate_management_app";
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mController = new CredentialManagementAppHeaderController(
+                mContext, PREF_KEY_CREDENTIAL_MANAGEMENT_APP);
+    }
+
+    @Test
+    public void getAvailabilityStatus_shouldAlwaysReturnAvailableUnsearchable() {
+        assertThat(mController.getAvailabilityStatus())
+                .isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
index 38756ac..3a159b2 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowLockPatternUtils.java
@@ -18,19 +18,31 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
+import android.os.UserHandle;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 @Implements(LockPatternUtils.class)
 public class ShadowLockPatternUtils {
 
     private static boolean sDeviceEncryptionEnabled;
+    private static Map<Integer, Integer> sUserToComplexityMap = new HashMap<>();
+
+
+    @Resetter
+    public static void reset() {
+        sUserToComplexityMap.clear();
+        sDeviceEncryptionEnabled = false;
+    }
 
     @Implementation
     protected boolean hasSecureLockScreen() {
@@ -76,4 +88,18 @@
     protected boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) {
         return false;
     }
+
+    @Implementation
+    public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) {
+        return sUserToComplexityMap.getOrDefault(userId,
+                DevicePolicyManager.PASSWORD_COMPLEXITY_NONE);
+    }
+
+    public static void setRequiredPasswordComplexity(int userId, int complexity) {
+        sUserToComplexityMap.put(userId, complexity);
+    }
+
+    public static void setRequiredPasswordComplexity(int complexity) {
+        setRequiredPasswordComplexity(UserHandle.myUserId(), complexity);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/widget/LabeledSeekBarTest.java b/tests/robotests/src/com/android/settings/widget/LabeledSeekBarTest.java
new file mode 100644
index 0000000..cd73089
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/widget/LabeledSeekBarTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.widget;
+
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class LabeledSeekBarTest {
+
+    private Context mContext;
+    private AttributeSet mAttrs;
+    private SeekBar mSeekBar;
+    private LabeledSeekBar mLabeledSeekBar;
+    private SeekBar.OnSeekBarChangeListener mProxySeekBarListener;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mSeekBar = new SeekBar(mContext, mAttrs);
+        mLabeledSeekBar = new LabeledSeekBar(mContext, mAttrs);
+        mProxySeekBarListener = shadowOf(mLabeledSeekBar).getOnSeekBarChangeListener();
+    }
+
+    @Test
+    public void onProgressChanged_minimumValue_clockTickFeedbackPerformed() {
+        mSeekBar.performHapticFeedback(CONTEXT_CLICK);
+        mProxySeekBarListener.onProgressChanged(mSeekBar, 0, true);
+
+        assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksPreferenceControllerTest.java
new file mode 100644
index 0000000..4c4c5bd
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksPreferenceControllerTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class AirplaneSafeNetworksPreferenceControllerTest {
+
+    private static final String KEY_AIRPLANE_SAFE_NETWORKS = "airplane_safe_networks";
+
+    private static final int ON = 1;
+    private static final int OFF = 0;
+
+    private ContentResolver mResolver;
+    private PreferenceScreen mScreen;
+    private RestrictedSwitchPreference mPreference;
+    private AirplaneSafeNetworksPreferenceController mController;
+
+    @Mock
+    private WifiManager mWifiManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context context = spy(ApplicationProvider.getApplicationContext());
+        mResolver = context.getContentResolver();
+        doReturn(mWifiManager).when(context).getSystemService(Context.WIFI_SERVICE);
+
+        mController = new AirplaneSafeNetworksPreferenceController(context, mock(Lifecycle.class));
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        final PreferenceManager preferenceManager = new PreferenceManager(context);
+        mScreen = preferenceManager.createPreferenceScreen(context);
+        mPreference = new RestrictedSwitchPreference(context);
+        mPreference.setKey(KEY_AIRPLANE_SAFE_NETWORKS);
+        mScreen.addPreference(mPreference);
+    }
+
+    @Test
+    public void isAvailable_airplaneModeOff_returnFalse() {
+        Settings.Global.putInt(mResolver, Settings.Global.AIRPLANE_MODE_ON, OFF);
+
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_airplaneModeOn_returnTrue() {
+        Settings.Global.putInt(mResolver, Settings.Global.AIRPLANE_MODE_ON, ON);
+
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isChecked_wifiStateDisabled_returnFalse() {
+        doReturn(WifiManager.WIFI_STATE_DISABLED).when(mWifiManager).getWifiState();
+
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void isChecked_wifiStateEnabled_returnTrue() {
+        doReturn(WifiManager.WIFI_STATE_ENABLED).when(mWifiManager).getWifiState();
+
+        mController.displayPreference(mScreen);
+        mController.onStart();
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksSliceTest.java b/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksSliceTest.java
new file mode 100644
index 0000000..ce47eb7
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/AirplaneSafeNetworksSliceTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceItem;
+import androidx.slice.SliceMetadata;
+import androidx.slice.SliceProvider;
+import androidx.slice.widget.SliceLiveData;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.AirplaneModeEnabler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class AirplaneSafeNetworksSliceTest {
+
+    private static final String VIEW_AIRPLANE_SAFE_NETWORKS = "View airplane-safe networks";
+    private static final String TURN_OFF_AIRPLANE_MODE = "Turn off Airplane Mode";
+
+    @Rule
+    public MockitoRule mMocks = MockitoJUnit.rule();
+    @Mock
+    private WifiManager mWifiManager;
+
+    private Context mContext;
+    private AirplaneModeEnabler mAirplaneModeEnabler;
+    private AirplaneSafeNetworksSlice mAirplaneSafeNetworksSlice;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        mAirplaneModeEnabler =
+                new AirplaneModeEnabler(mContext, null /* OnAirplaneModeChangedListener */);
+
+        // Set-up specs for SliceMetadata.
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+
+        mAirplaneSafeNetworksSlice = new AirplaneSafeNetworksSlice(mContext);
+    }
+
+    @Test
+    public void getSlice_airplaneModeOff_shouldBeNull() {
+        mAirplaneModeEnabler.setAirplaneMode(false);
+
+        assertThat(mAirplaneSafeNetworksSlice.getSlice()).isNull();
+    }
+
+    @Test
+    public void getSlice_wifiDisabled_shouldShowViewAirplaneSafeNetworks() {
+        mAirplaneModeEnabler.setAirplaneMode(true);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+        final Slice slice = mAirplaneSafeNetworksSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        final SliceItem sliceTitle =
+                SliceMetadata.from(mContext, slice).getListContent().getHeader().getTitleItem();
+        assertThat(sliceTitle.getText()).isEqualTo(VIEW_AIRPLANE_SAFE_NETWORKS);
+    }
+
+    @Test
+    public void getSlice_wifiEnabled_shouldShowTurnOffAirplaneMode() {
+        mAirplaneModeEnabler.setAirplaneMode(true);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+        final Slice slice = mAirplaneSafeNetworksSlice.getSlice();
+
+        assertThat(slice).isNotNull();
+        final SliceItem sliceTitle =
+                SliceMetadata.from(mContext, slice).getListContent().getHeader().getTitleItem();
+        assertThat(sliceTitle.getText()).isEqualTo(TURN_OFF_AIRPLANE_MODE);
+    }
+
+    @Test
+    public void onNotifyChange_viewAirplaneSafeNetworks_shouldSetWifiEnabled() {
+        mAirplaneModeEnabler.setAirplaneMode(true);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+        Intent intent = mAirplaneSafeNetworksSlice.getIntent();
+
+        mAirplaneSafeNetworksSlice.onNotifyChange(intent);
+
+        verify(mWifiManager).setWifiEnabled(true);
+    }
+
+    @Test
+    public void onNotifyChange_turnOffAirplaneMode_shouldSetAirplaneModeOff() {
+        mAirplaneModeEnabler.setAirplaneMode(true);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        Intent intent = mAirplaneSafeNetworksSlice.getIntent();
+
+        mAirplaneSafeNetworksSlice.onNotifyChange(intent);
+
+        assertThat(mAirplaneModeEnabler.isAirplaneModeOn()).isFalse();
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/NetworkMobileProviderControllerTest.java b/tests/unit/src/com/android/settings/network/NetworkMobileProviderControllerTest.java
new file mode 100644
index 0000000..31c68da
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/NetworkMobileProviderControllerTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+import static com.android.settings.network.NetworkMobileProviderController.PREF_KEY_PROVIDER_MOBILE_NETWORK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test for NetworkMobileProviderController.
+ *
+ * {@link NetworkMobileProviderController} is used to show subscription status on internet page for
+ * provider model. This original class can refer to {@link MultiNetworkHeaderController}, and
+ * NetworkMobileProviderControllerTest can also refer to {@link MultiNetworkHeaderControllerTest}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class NetworkMobileProviderControllerTest {
+
+    private static final int EXPANDED_CHILDREN_COUNT = 3;
+
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private PreferenceCategory mPreferenceCategory;
+    @Mock
+    private SubscriptionsPreferenceController mSubscriptionsController;
+
+    private Context mContext;
+    private PreferenceManager mPreferenceManager;
+    private PreferenceScreen mPreferenceScreen;
+    private NetworkMobileProviderController mNetworkMobileProviderController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        when(mPreferenceCategory.getKey()).thenReturn(PREF_KEY_PROVIDER_MOBILE_NETWORK);
+        when(mPreferenceCategory.getPreferenceCount()).thenReturn(3);
+
+        mContext = ApplicationProvider.getApplicationContext();
+        mNetworkMobileProviderController =
+                new NetworkMobileProviderController(mContext, PREF_KEY_PROVIDER_MOBILE_NETWORK) {
+            @Override
+            SubscriptionsPreferenceController createSubscriptionsController(
+                    Lifecycle lifecycle) {
+                return mSubscriptionsController;
+            }
+        };
+
+        mPreferenceManager = new PreferenceManager(mContext);
+        mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+        mPreferenceScreen.setInitialExpandedChildrenCount(EXPANDED_CHILDREN_COUNT);
+        mPreferenceScreen.addPreference(mPreferenceCategory);
+    }
+
+    @Test
+    public void testDisplayPreference_subscriptionsControllerAvailable() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(true);
+        setupNetworkMobileProviderController();
+
+        assertTrue(mPreferenceCategory.isVisible());
+    }
+
+    @Test
+    public void testDisplayPreference_subscriptionsControllerUnAvailable() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(false);
+        setupNetworkMobileProviderController();
+
+        assertFalse(mPreferenceCategory.isVisible());
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_subscriptionsControllerIsNull() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(false);
+        mNetworkMobileProviderController = new NetworkMobileProviderController(mContext,
+                PREF_KEY_PROVIDER_MOBILE_NETWORK) {
+            @Override
+            SubscriptionsPreferenceController createSubscriptionsController(Lifecycle lifecycle) {
+                return null;
+            }
+        };
+        setupNetworkMobileProviderController();
+
+        final int result = mNetworkMobileProviderController.getAvailabilityStatus();
+
+        assertEquals(result, CONDITIONALLY_UNAVAILABLE);
+    }
+
+    @Test
+    public void testGetAvailabilityStatus_subscriptionsControllerAvailable() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(true);
+        setupNetworkMobileProviderController();
+
+        final int result = mNetworkMobileProviderController.getAvailabilityStatus();
+
+        assertEquals(result, AVAILABLE);
+    }
+
+    @Test
+    public void testOnChildUpdated_subscriptionsControllerAvailable_categoryIsVisible() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(true);
+        setupNetworkMobileProviderController();
+
+        mNetworkMobileProviderController.onChildrenUpdated();
+
+        assertTrue(mPreferenceCategory.isVisible());
+        assertThat(mPreferenceScreen.getInitialExpandedChildrenCount()).isEqualTo(
+                EXPANDED_CHILDREN_COUNT + mPreferenceCategory.getPreferenceCount());
+    }
+
+    @Test
+    public void testOnChildUpdated_subscriptionsControllerUnavailable_categoryIsInvisible() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(false);
+        setupNetworkMobileProviderController();
+
+        mNetworkMobileProviderController.onChildrenUpdated();
+
+        assertFalse(mPreferenceCategory.isVisible());
+        assertThat(mPreferenceScreen.getInitialExpandedChildrenCount()).isEqualTo(
+                EXPANDED_CHILDREN_COUNT);
+    }
+
+    @Test
+    public void testOnChildUpdated_noExpandedChildCountAndAvailable_doesNotSetExpandedCount() {
+        when(mSubscriptionsController.isAvailable()).thenReturn(true);
+        mPreferenceScreen.setInitialExpandedChildrenCount(Integer.MAX_VALUE);
+        setupNetworkMobileProviderController();
+
+        mNetworkMobileProviderController.onChildrenUpdated();
+
+        assertEquals(mPreferenceScreen.getInitialExpandedChildrenCount(), Integer.MAX_VALUE);
+    }
+
+    private void setupNetworkMobileProviderController() {
+        mNetworkMobileProviderController.init(mLifecycle);
+        mNetworkMobileProviderController.displayPreference(mPreferenceScreen);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
index 39c7bbd..34849b9 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkSwitchControllerTest.java
@@ -18,22 +18,27 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 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.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
 import android.os.Looper;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.test.UiThreadTest;
 
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -47,6 +52,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -168,9 +174,15 @@
         assertThat(mSwitchBar.isShowing()).isTrue();
         assertThat(mSwitchBar.isChecked()).isTrue();
 
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        doNothing().when(mContext).startActivity(intentCaptor.capture());
         mSwitchBar.setChecked(false);
+        Bundle extra = intentCaptor.getValue().getExtras();
 
-        verify(mSubscriptionManager).setSubscriptionEnabled(eq(mSubId), eq(false));
+        verify(mContext, times(1)).startActivity(any());
+        assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
+        assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable))
+                .isEqualTo(false);
     }
 
     @Test
@@ -183,9 +195,15 @@
         assertThat(mSwitchBar.isShowing()).isTrue();
         assertThat(mSwitchBar.isChecked()).isTrue();
 
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        doNothing().when(mContext).startActivity(intentCaptor.capture());
         mSwitchBar.setChecked(false);
+        Bundle extra = intentCaptor.getValue().getExtras();
 
-        verify(mSubscriptionManager).setSubscriptionEnabled(eq(mSubId), eq(false));
+        verify(mContext, times(1)).startActivity(any());
+        assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
+        assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable))
+                .isEqualTo(false);
         assertThat(mSwitchBar.isChecked()).isTrue();
     }
 
@@ -197,8 +215,13 @@
         assertThat(mSwitchBar.isShowing()).isTrue();
         assertThat(mSwitchBar.isChecked()).isFalse();
 
+        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        doNothing().when(mContext).startActivity(intentCaptor.capture());
         mSwitchBar.setChecked(true);
+        Bundle extra = intentCaptor.getValue().getExtras();
 
-        verify(mSubscriptionManager).setSubscriptionEnabled(eq(mSubId), eq(true));
+        verify(mContext, times(1)).startActivity(any());
+        assertThat(extra.getInt(ToggleSubscriptionDialogActivity.ARG_SUB_ID)).isEqualTo(mSubId);
+        assertThat(extra.getBoolean(ToggleSubscriptionDialogActivity.ARG_enable)).isEqualTo(true);
     }
 }