Merge "Extract magnification capabilities from MagnificationSettingsFragment into a new class."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c77680d..c946122 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2742,6 +2742,19 @@
         </activity>
 
         <activity
+            android:name="Settings$PremiumSmsAccessActivity"
+            android:label="@string/premium_sms_access"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.settings.PREMIUM_SMS_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                       android:value="com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess" />
+        </activity>
+
+        <activity
             android:name="Settings$SoundSettingsActivity"
             android:label="@string/sound_settings"
             android:icon="@drawable/ic_homepage_sound"
diff --git a/res/layout/settings_homepage_container.xml b/res/layout/settings_homepage_container.xml
index f210c54..fe119c6 100644
--- a/res/layout/settings_homepage_container.xml
+++ b/res/layout/settings_homepage_container.xml
@@ -57,6 +57,41 @@
         android:layout_height="wrap_content"
         android:touchscreenBlocksFocus="false"
         android:keyboardNavigationCluster="false">
-        <include layout="@layout/search_bar"/>
+        <LinearLayout
+            android:id="@+id/app_bar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
+
+            <LinearLayout
+                android:id="@+id/contextual_suggestion_content"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/suggestion_height"
+                android:paddingHorizontal="@dimen/suggestion_padding_horizontal"
+                android:paddingBottom="@dimen/suggestion_padding_bottom"
+                android:orientation="vertical"
+                android:gravity="bottom"
+                android:visibility="gone">
+
+                <TextView
+                    android:id="@+id/suggestion_title"
+                    android:text="@string/settings_label"
+                    style="@style/ContextualSuggestionText"/>
+
+                <Button
+                    android:id="@+id/suggestion_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/suggestion_button_margin_top"
+                    android:paddingHorizontal="@dimen/suggestion_button_padding_horizontal"
+                    android:visibility="gone"
+                    style="@style/ActionPrimaryButton"/>
+
+            </LinearLayout>
+
+            <include layout="@layout/search_bar"/>
+
+        </LinearLayout>
     </com.google.android.material.appbar.AppBarLayout>
 </androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/res/values-th/arrays.xml b/res/values-th/arrays.xml
index d9ebe44..3b533bb 100644
--- a/res/values-th/arrays.xml
+++ b/res/values-th/arrays.xml
@@ -233,8 +233,8 @@
     <item msgid="50701215019227883">"สั่น"</item>
     <item msgid="2690144000353492014">"อ่านรายชื่อติดต่อ"</item>
     <item msgid="3858029424955955625">"แก้ไขรายชื่อติดต่อ"</item>
-    <item msgid="3439658954936709507">"อ่านประวัติการโทร"</item>
-    <item msgid="1908944516631132130">"แก้ไขประวัติการโทร"</item>
+    <item msgid="3439658954936709507">"อ่านบันทึกการโทร"</item>
+    <item msgid="1908944516631132130">"แก้ไขบันทึกการโทร"</item>
     <item msgid="9066115715905100138">"อ่านปฏิทิน"</item>
     <item msgid="1664720478157892566">"แก้ไขปฏิทิน"</item>
     <item msgid="5478277451617814822">"ค้นหา WiFi"</item>
@@ -300,8 +300,8 @@
     <item msgid="4104617224667554750">"สั่น"</item>
     <item msgid="1623646715189708947">"อ่านรายชื่อติดต่อ"</item>
     <item msgid="5060760609109972207">"แก้ไขรายชื่อติดต่อ"</item>
-    <item msgid="7451260062940797278">"อ่านประวัติการโทร"</item>
-    <item msgid="2348589304974534308">"แก้ไขประวัติการโทร"</item>
+    <item msgid="7451260062940797278">"อ่านบันทึกการโทร"</item>
+    <item msgid="2348589304974534308">"แก้ไขบันทึกการโทร"</item>
     <item msgid="4089146706115315300">"อ่านปฏิทิน"</item>
     <item msgid="1305780729690198918">"แก้ไขปฏิทิน"</item>
     <item msgid="3461096740171440592">"ตำแหน่ง"</item>
@@ -495,7 +495,7 @@
   </string-array>
   <string-array name="wifi_metered_entries">
     <item msgid="3237321077949659241">"ตรวจหาโดยอัตโนมัติ"</item>
-    <item msgid="3779092145391320375">"ถือว่ามีการวัดปริมาณอินเทอร์เน็ต"</item>
+    <item msgid="3779092145391320375">"ถือว่ามีการจำกัดปริมาณอินเทอร์เน็ต"</item>
     <item msgid="2047166446768045816">"ถือว่าไม่มีการวัดปริมาณอินเทอร์เน็ต"</item>
   </string-array>
   <string-array name="wifi_privacy_entries">
diff --git a/res/values-uz/arrays.xml b/res/values-uz/arrays.xml
index d2f82b7..9d885b2 100644
--- a/res/values-uz/arrays.xml
+++ b/res/values-uz/arrays.xml
@@ -499,8 +499,8 @@
     <item msgid="2047166446768045816">"Bu – bepul tarmoq"</item>
   </string-array>
   <string-array name="wifi_privacy_entries">
-    <item msgid="3485945604919292489">"Ixtiyoriy MAC manzil (standart)"</item>
-    <item msgid="741680937828608749">"Ixtiyoriy MAC manzil"</item>
+    <item msgid="3485945604919292489">"Tasodifiy MAC manzil (standart)"</item>
+    <item msgid="741680937828608749">"Qurilmaning MAC manzili"</item>
   </string-array>
   <string-array name="wifi_hidden_entries">
     <item msgid="342232116597649254">"Yo‘q"</item>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index f8df920..bf757d1 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -223,6 +223,10 @@
     <!-- Security types for wireless tether -->
     <string-array name="wifi_tether_security">
         <!-- Do not translate. -->
+        <item>@string/wifi_security_sae</item>
+        <!-- Do not translate. -->
+        <item>@string/wifi_security_psk_sae</item>
+        <!-- Do not translate. -->
         <item>@string/wifi_security_wpa2</item>
         <!-- Do not translate. -->
         <item>@string/wifi_security_none</item>
@@ -231,6 +235,10 @@
     <!-- Values for security type for wireless tether -->
     <string-array name="wifi_tether_security_values" translatable="false">
         <!-- Do not translate. -->
+        <item>3</item>
+        <!-- Do not translate. -->
+        <item>2</item>
+        <!-- Do not translate. -->
         <item>1</item>
         <!-- Do not translate. -->
         <item>0</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 7809f05..bc01d59 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -123,13 +123,20 @@
     <dimen name="switchbar_subsettings_margin_start">72dp</dimen>
     <dimen name="switchbar_subsettings_margin_end">16dp</dimen>
 
+    <!-- Search bar and avatar -->
     <dimen name="search_bar_margin">24dp</dimen>
     <dimen name="search_bar_height">48dp</dimen>
-    <dimen name="search_bar_text_size">16dp</dimen>
+    <dimen name="search_bar_text_size">16sp</dimen>
     <dimen name="search_bar_card_elevation">2dp</dimen>
     <dimen name="search_bar_content_inset">64dp</dimen>
     <dimen name="avatar_length">@dimen/search_bar_height</dimen>
 
+    <!-- Contextual suggestions -->
+    <dimen name="suggestion_height">224dp</dimen>
+    <dimen name="suggestion_padding_horizontal">24dp</dimen>
+    <dimen name="suggestion_padding_bottom">8dp</dimen>
+    <dimen name="suggestion_button_margin_top">16dp</dimen>
+    <dimen name="suggestion_button_padding_horizontal">24dp</dimen>
 
     <!-- Dimensions for Wifi Assistant Card -->
     <dimen name="wifi_assistant_padding_top_bottom">16dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index fd4d268..ce6b095 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -782,6 +782,14 @@
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
     </style>
 
+    <style name="ContextualSuggestionText" parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textSize">32sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+    </style>
+
     <style name="RequestManageCredentialsButtonPanel">
         <item name="android:paddingStart">12dp</item>
         <item name="android:paddingEnd">12dp</item>
diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml
index 98ab28d..959ffb3 100644
--- a/res/values/themes_suw.xml
+++ b/res/values/themes_suw.xml
@@ -197,8 +197,8 @@
         <item name="android:textAllCaps">false</item>
 
         <item name="android:windowSoftInputMode">adjustResize</item>
-        <!-- copied from Theme.DeviceDefault.Light.Dialog.Alert -->
-        <item name="colorAccent">@*android:color/accent_device_default_light</item>
+        <!-- copied from Theme.DeviceDefault.Dialog.Alert -->
+        <item name="colorAccent">@*android:color/accent_device_default_dark</item>
         <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
     </style>
 
diff --git a/res/xml/display_settings_v2.xml b/res/xml/display_settings_v2.xml
new file mode 100644
index 0000000..5e20db5
--- /dev/null
+++ b/res/xml/display_settings_v2.xml
@@ -0,0 +1,143 @@
+<?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:key="display_settings_screen"
+    android:title="@string/display_settings"
+    settings:keywords="@string/keywords_display"
+    settings:initialExpandedChildrenCount="5">
+
+    <com.android.settingslib.RestrictedPreference
+        android:key="brightness"
+        android:title="@string/brightness"
+        settings:keywords="@string/keywords_display_brightness_level"
+        settings:useAdminDisabledSummary="true"
+        settings:userRestriction="no_config_brightness">
+        <intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
+    </com.android.settingslib.RestrictedPreference>
+
+    <com.android.settings.display.darkmode.DarkModePreference
+        android:key="dark_ui_mode"
+        android:title="@string/dark_ui_mode"
+        android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"
+        android:widgetLayout="@null"
+        settings:widgetLayout="@null"
+        settings:controller="com.android.settings.display.DarkUIPreferenceController"
+        settings:keywords="@string/keywords_dark_ui_mode"/>
+
+    <com.android.settings.display.NightDisplayPreference
+        android:key="night_display"
+        android:title="@string/night_display_title"
+        android:fragment="com.android.settings.display.NightDisplaySettings"
+        android:widgetLayout="@null"
+        settings:widgetLayout="@null"/>
+
+    <Preference
+        android:key="auto_brightness_entry"
+        android:title="@string/auto_brightness_title"
+        android:summary="@string/summary_placeholder"
+        android:fragment="com.android.settings.display.AutoBrightnessSettings"
+        settings:controller="com.android.settings.display.AutoBrightnessPreferenceController"/>
+
+    <!-- Cross-listed item, if you change this, also change it in power_usage_summary.xml -->
+    <com.android.settingslib.RestrictedPreference
+        android:key="screen_timeout"
+        android:title="@string/screen_timeout"
+        android:summary="@string/summary_placeholder"
+        android:fragment="com.android.settings.display.ScreenTimeoutSettings"
+        settings:controller="com.android.settings.display.ScreenTimeoutPreferenceController" />
+
+    <SwitchPreference
+        android:key="auto_rotate"
+        android:title="@string/accelerometer_title"
+        settings:keywords="@string/keywords_auto_rotate"
+        settings:controller="com.android.settings.display.AutoRotatePreferenceController" />
+
+    <Preference
+        android:key="color_mode"
+        android:title="@string/color_mode_title"
+        android:fragment="com.android.settings.display.ColorModePreferenceFragment"
+        settings:controller="com.android.settings.display.ColorModePreferenceController"
+        settings:keywords="@string/keywords_color_mode" />
+
+    <SwitchPreference
+        android:key="display_white_balance"
+        android:title="@string/display_white_balance_title"
+        android:summary="@string/display_white_balance_summary"
+        settings:controller="com.android.settings.display.DisplayWhiteBalancePreferenceController" />
+
+    <SwitchPreference
+        android:key="peak_refresh_rate"
+        android:title="@string/peak_refresh_rate_title"
+        android:summary="@string/peak_refresh_rate_summary"
+        settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController" />
+
+    <Preference
+        android:key="font_size"
+        android:title="@string/title_font_size"
+        android:fragment="com.android.settings.display.ToggleFontSizePreferenceFragment"
+        settings:controller="com.android.settings.display.FontSizePreferenceController"
+        settings:keywords="@string/keywords_font_size" />
+
+    <com.android.settings.display.ScreenZoomPreference
+        android:key="display_settings_screen_zoom"
+        android:title="@string/screen_zoom_title"
+        android:fragment="com.android.settings.display.ScreenZoomSettings"/>
+
+    <SwitchPreference
+        android:key="show_operator_name"
+        android:title="@string/show_operator_name_title"
+        android:summary="@string/show_operator_name_summary" />
+
+    <Preference
+        android:key="screensaver"
+        android:title="@string/screensaver_settings_title"
+        android:fragment="com.android.settings.dream.DreamSettings" />
+
+    <Preference
+        android:key="lockscreen_from_display_settings"
+        android:title="@string/lockscreen_settings_title"
+        android:fragment="com.android.settings.security.LockscreenDashboardFragment"
+        settings:keywords="@string/keywords_ambient_display_screen"
+        settings:controller="com.android.settings.security.screenlock.LockScreenPreferenceController" />
+
+    <SwitchPreference
+        android:key="camera_gesture"
+        android:title="@string/camera_gesture_title"
+        android:summary="@string/camera_gesture_desc" />
+
+    <SwitchPreference
+        android:key="lift_to_wake"
+        android:title="@string/lift_to_wake_title" />
+
+    <SwitchPreference
+        android:key="tap_to_wake"
+        android:title="@string/tap_to_wake"
+        android:summary="@string/tap_to_wake_summary" />
+
+    <ListPreference
+        android:key="theme"
+        android:title="@string/device_theme"
+        android:summary="@string/summary_placeholder" />
+
+    <Preference
+        android:key="vr_display_pref"
+        android:title="@string/display_vr_pref_title"
+        android:fragment="com.android.settings.display.VrDisplayPreferencePicker" />
+
+</PreferenceScreen>
diff --git a/res/xml/top_level_settings_grouped.xml b/res/xml/top_level_settings_grouped.xml
index cdfab91..baee7b3 100644
--- a/res/xml/top_level_settings_grouped.xml
+++ b/res/xml/top_level_settings_grouped.xml
@@ -20,43 +20,38 @@
     xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:key="top_level_settings_grouped">
 
-    <PreferenceCategory
-        android:key="connectivity"
-        android:order="-130"
-        settings:allowDividerAbove="false">
-        <Preference
-            android:fragment="com.android.settings.network.NetworkDashboardFragment"
-            android:icon="@drawable/ic_homepage_network"
-            android:key="top_level_network"
-            android:order="-130"
-            android:title="@string/network_dashboard_title"
-            settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
+    <Preference
+        android:fragment="com.android.settings.network.NetworkDashboardFragment"
+        android:icon="@drawable/ic_homepage_network"
+        android:key="top_level_network"
+        android:order="-140"
+        android:title="@string/network_dashboard_title"
+        settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
 
-        <Preference
-            android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
-            android:icon="@drawable/ic_homepage_connected_device"
-            android:key="top_level_connected_devices"
-            android:order="-120"
-            android:title="@string/connected_devices_dashboard_title"
-            settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
-    </PreferenceCategory>
+    <Preference
+        android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
+        android:icon="@drawable/ic_homepage_connected_device"
+        android:key="top_level_connected_devices"
+        android:order="-130"
+        android:title="@string/connected_devices_dashboard_title"
+        settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
 
     <PreferenceCategory
         android:key="apps"
-        android:order="-110"
+        android:order="-120"
         settings:allowDividerAbove="false">
         <Preference
             android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"
             android:icon="@drawable/ic_homepage_apps"
             android:key="top_level_apps_and_notifs"
-            android:order="-110"
+            android:order="-120"
             android:title="@string/apps_dashboard_title"/>
 
         <Preference
             android:fragment="com.android.settings.notification.ConfigureNotificationSettings"
             android:icon="@drawable/ic_homepage_notification"
             android:key="top_level_notification"
-            android:order="-100"
+            android:order="-110"
             android:title="@string/configure_notification_settings"/>
 
 
@@ -64,13 +59,13 @@
 
     <PreferenceCategory
         android:key="phone_essential"
-        android:order="-90"
+        android:order="-100"
         settings:allowDividerAbove="false">
         <Preference
             android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
             android:icon="@drawable/ic_homepage_battery"
             android:key="top_level_battery"
-            android:order="-90"
+            android:order="-100"
             android:title="@string/power_usage_summary_title"
             settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
 
@@ -78,7 +73,7 @@
             android:fragment="com.android.settings.deviceinfo.StorageSettings"
             android:icon="@drawable/ic_homepage_storage"
             android:key="top_level_storage"
-            android:order="-80"
+            android:order="-90"
             android:title="@string/storage_settings"
             settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
 
@@ -86,17 +81,24 @@
             android:fragment="com.android.settings.notification.SoundSettings"
             android:icon="@drawable/ic_homepage_sound"
             android:key="top_level_sound"
-            android:order="-70"
+            android:order="-80"
             android:title="@string/sound_settings"/>
 
         <Preference
             android:fragment="com.android.settings.DisplaySettings"
             android:icon="@drawable/ic_homepage_display"
             android:key="top_level_display"
-            android:order="-60"
+            android:order="-70"
             android:title="@string/display_settings"
             settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
 
+        <com.android.settingslib.RestrictedTopLevelPreference
+            android:icon="@drawable/ic_homepage_display"
+            android:key="top_level_wallpaper"
+            android:order="-60"
+            android:title="@string/wallpaper_settings_title"
+            settings:controller="com.android.settings.display.TopLevelWallpaperPreferenceController"/>
+
         <Preference
             android:fragment="com.android.settings.accessibility.AccessibilitySettings"
             android:icon="@drawable/ic_homepage_accessibility"
diff --git a/src/com/android/settings/AllInOneTetherSettings.java b/src/com/android/settings/AllInOneTetherSettings.java
index 82e3206..5442ed7 100644
--- a/src/com/android/settings/AllInOneTetherSettings.java
+++ b/src/com/android/settings/AllInOneTetherSettings.java
@@ -355,7 +355,7 @@
     @Override
     public void onTetherConfigUpdated(AbstractPreferenceController controller) {
         final SoftApConfiguration config = buildNewConfig();
-        mPasswordPreferenceController.updateVisibility(config.getSecurityType());
+        mPasswordPreferenceController.setSecurityType(config.getSecurityType());
         mWifiManager.setSoftApConfiguration(config);
 
         if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index cbfbe7a..3292ede 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -19,7 +19,10 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+import android.util.FeatureFlagUtils;
 
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.display.BrightnessLevelPreferenceController;
 import com.android.settings.display.CameraGesturePreferenceController;
@@ -37,6 +40,7 @@
 import com.android.settingslib.search.SearchIndexable;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -55,6 +59,9 @@
 
     @Override
     protected int getPreferenceScreenResId() {
+        if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.SILKY_HOME)) {
+            return R.xml.display_settings_v2;
+        }
         return R.xml.display_settings;
     }
 
@@ -90,7 +97,16 @@
     }
 
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider(R.xml.display_settings) {
+            new BaseSearchIndexProvider() {
+
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME)
+                            ? R.xml.display_settings_v2 : R.xml.display_settings;
+                    return Arrays.asList(sir);
+                }
 
                 @Override
                 public List<AbstractPreferenceController> createPreferenceControllers(
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b226133..8389d84 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -144,6 +144,7 @@
     public static class NotificationAccessSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationAccessDetailsActivity extends SettingsActivity { /* empty */ }
     public static class VrListenersSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class PremiumSmsAccessActivity extends SettingsActivity { /* empty */ }
     public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
     public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java
index b9af7ce..78f31df 100644
--- a/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java
+++ b/src/com/android/settings/accessibility/ToggleAutoclickPreferenceController.java
@@ -25,6 +25,7 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -44,16 +45,23 @@
 public class ToggleAutoclickPreferenceController extends BasePreferenceController implements
         LifecycleObserver, RadioButtonPreference.OnClickListener, PreferenceControllerMixin {
 
-    private static final String CONTROL_AUTOCLICK_DELAY_SECURE =
+    @VisibleForTesting
+    static final String CONTROL_AUTOCLICK_DELAY_SECURE =
             Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY;
-    private static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar";
+
+    @VisibleForTesting
+    static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar";
     static final String KEY_DELAY_MODE = "delay_mode";
 
-    private static final int AUTOCLICK_OFF_MODE = 0;
-    private static final int AUTOCLICK_CUSTOM_MODE = 2000;
+    @VisibleForTesting
+    static final int AUTOCLICK_OFF_MODE = 0;
+
+    @VisibleForTesting
+    static final int AUTOCLICK_CUSTOM_MODE = 2000;
 
     // Pair the preference key and autoclick mode value.
-    private final Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>();
+    @VisibleForTesting
+    Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>();
 
     private SharedPreferences mSharedPreferences;
     private final ContentResolver mContentResolver;
@@ -70,13 +78,7 @@
     private int mCurrentUiAutoClickMode;
 
     public ToggleAutoclickPreferenceController(Context context, String preferenceKey) {
-        super(context, preferenceKey);
-
-        mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE);
-        mContentResolver = context.getContentResolver();
-        mResources = context.getResources();
-
-        setAutoclickModeToKeyMap();
+        this(context, /* lifecycle= */ null, preferenceKey);
     }
 
     public ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle,
diff --git a/src/com/android/settings/accessibility/VideoPlayer.java b/src/com/android/settings/accessibility/VideoPlayer.java
index 8f94b76..d4aa6a6 100644
--- a/src/com/android/settings/accessibility/VideoPlayer.java
+++ b/src/com/android/settings/accessibility/VideoPlayer.java
@@ -25,6 +25,7 @@
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.RawRes;
+import androidx.annotation.VisibleForTesting;
 
 /**
  * Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int,
@@ -32,19 +33,25 @@
  * is no longer used, call {@link #release()} so that MediaPlayer object can be released.
  */
 public class VideoPlayer implements SurfaceTextureListener {
-    private final Context context;
-    private final Object mediaPlayerLock = new Object();
+    private final Context mContext;
+    private final Object mMediaPlayerLock = new Object();
     // Media player object can't be used after it has been released, so it will be set to null. But
     // VideoPlayer is asynchronized, media player object might be paused or resumed again before
     // released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by
     // mediaPlayerLock keep their states consistent.
+    @VisibleForTesting
     @GuardedBy("mediaPlayerLock")
-    private MediaPlayer mediaPlayer;
+    MediaPlayer mMediaPlayer;
+
+    @VisibleForTesting
     @GuardedBy("mediaPlayerLock")
-    private State mediaPlayerState = State.NONE;
+    State mMediaPlayerState = State.NONE;
+
     @RawRes
-    private final int videoRes;
-    private Surface animationSurface;
+    private final int mVideoRes;
+
+    @VisibleForTesting
+    Surface mAnimationSurface;
 
 
     /**
@@ -58,54 +65,54 @@
     }
 
     private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) {
-        this.context = context;
-        this.videoRes = videoRes;
+        this.mContext = context;
+        this.mVideoRes = videoRes;
         textureView.setSurfaceTextureListener(this);
     }
 
     public void pause() {
-        synchronized (mediaPlayerLock) {
-            if (mediaPlayerState == State.STARTED) {
-                mediaPlayerState = State.PAUSED;
-                mediaPlayer.pause();
+        synchronized (mMediaPlayerLock) {
+            if (mMediaPlayerState == State.STARTED) {
+                mMediaPlayerState = State.PAUSED;
+                mMediaPlayer.pause();
             }
         }
     }
 
     public void resume() {
-        synchronized (mediaPlayerLock) {
-            if (mediaPlayerState == State.PAUSED) {
-                mediaPlayer.start();
-                mediaPlayerState = State.STARTED;
+        synchronized (mMediaPlayerLock) {
+            if (mMediaPlayerState == State.PAUSED) {
+                mMediaPlayer.start();
+                mMediaPlayerState = State.STARTED;
             }
         }
     }
 
     /** Release media player when it's no longer needed. */
     public void release() {
-        synchronized (mediaPlayerLock) {
-            if (mediaPlayerState != State.NONE && mediaPlayerState != State.END) {
-                mediaPlayerState = State.END;
-                mediaPlayer.release();
-                mediaPlayer = null;
+        synchronized (mMediaPlayerLock) {
+            if (mMediaPlayerState != State.NONE && mMediaPlayerState != State.END) {
+                mMediaPlayerState = State.END;
+                mMediaPlayer.release();
+                mMediaPlayer = null;
             }
         }
-        if (animationSurface != null) {
-            animationSurface.release();
-            animationSurface = null;
+        if (mAnimationSurface != null) {
+            mAnimationSurface.release();
+            mAnimationSurface = null;
         }
     }
 
     @Override
     public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-        animationSurface = new Surface(surface);
-        synchronized (mediaPlayerLock) {
-            mediaPlayer = MediaPlayer.create(context, videoRes);
-            mediaPlayerState = State.PREPARED;
-            mediaPlayer.setSurface(animationSurface);
-            mediaPlayer.setLooping(true);
-            mediaPlayer.start();
-            mediaPlayerState = State.STARTED;
+        mAnimationSurface = new Surface(surface);
+        synchronized (mMediaPlayerLock) {
+            mMediaPlayer = MediaPlayer.create(mContext, mVideoRes);
+            mMediaPlayerState = State.PREPARED;
+            mMediaPlayer.setSurface(mAnimationSurface);
+            mMediaPlayer.setLooping(true);
+            mMediaPlayer.start();
+            mMediaPlayerState = State.STARTED;
         }
     }
 
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 25be4fa..2c21771 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -53,6 +53,7 @@
 import com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails;
 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetails;
 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureSettings;
+import com.android.settings.applications.specialaccess.premiumsms.PremiumSmsAccess;
 import com.android.settings.applications.specialaccess.vrlistener.VrListenerSettings;
 import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails;
 import com.android.settings.backup.PrivacySettings;
@@ -270,6 +271,7 @@
             VrListenerSettings.class.getName(),
             PictureInPictureSettings.class.getName(),
             PictureInPictureDetails.class.getName(),
+            PremiumSmsAccess.class.getName(),
             ManagedProfileSettings.class.getName(),
             ChooseAccountFragment.class.getName(),
             IccLockSettings.class.getName(),
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
index b755ba7..fe97759 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java
@@ -21,6 +21,7 @@
 import android.content.SharedPreferences;
 
 import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
 
 /** Interface should be implemented if you have added new suggestions */
 public interface SuggestionFeatureProvider {
@@ -42,4 +43,9 @@
      * Returns the {@link SharedPreferences} that holds metadata for suggestions.
      */
     SharedPreferences getSharedPrefs(Context context);
+
+    /**
+     * Returns the class of {@link Fragment} that supports contextual suggestion.
+     */
+    Class<? extends Fragment> getContextualSuggestionFragment();
 }
diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
index 60a8c54..deb1c7e 100644
--- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java
@@ -22,6 +22,7 @@
 import android.content.SharedPreferences;
 
 import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
 
 import com.android.settings.Settings.NightDisplaySuggestionActivity;
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollSuggestionActivity;
@@ -86,6 +87,11 @@
         return context.getSharedPreferences(SHARED_PREF_FILENAME, Context.MODE_PRIVATE);
     }
 
+    @Override
+    public Class<? extends Fragment> getContextualSuggestionFragment() {
+        return null;
+    }
+
     public SuggestionFeatureProviderImpl(Context context) {
         final Context appContext = context.getApplicationContext();
         mMetricsFeatureProvider = FeatureFactory.getFactory(appContext)
diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java
index 955c60c..e5fd3da 100644
--- a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareRevisionPreferenceController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
@@ -31,8 +32,8 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mContext.getResources().getBoolean(R.bool.config_show_device_model)
-                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+        return mContext.getResources().getBoolean(R.bool.config_show_device_model) &&
+                !TextUtils.isEmpty(getSummary()) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
diff --git a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
new file mode 100644
index 0000000..beef4f3
--- /dev/null
+++ b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import static android.os.UserManager.DISALLOW_SET_WALLPAPER;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.RestrictedTopLevelPreference;
+
+import java.util.List;
+
+/** This controller manages the wallpaper preference of the top level page. */
+public class TopLevelWallpaperPreferenceController extends BasePreferenceController {
+    private static final String TAG = "TopLevelWallpaperPreferenceController";
+    private static final String LAUNCHED_SETTINGS = "app_launched_settings";
+
+    private final String mWallpaperPackage;
+    private final String mWallpaperClass;
+    private final String mStylesAndWallpaperClass;
+    private final String mWallpaperLaunchExtra;
+
+    public TopLevelWallpaperPreferenceController(Context context, String key) {
+        super(context, key);
+        mWallpaperPackage = mContext.getString(R.string.config_wallpaper_picker_package);
+        mWallpaperClass = mContext.getString(R.string.config_wallpaper_picker_class);
+        mStylesAndWallpaperClass =
+                mContext.getString(R.string.config_styles_and_wallpaper_picker_class);
+        mWallpaperLaunchExtra = mContext.getString(R.string.config_wallpaper_picker_launch_extra);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Preference preference = screen.findPreference(getPreferenceKey());
+        preference.setTitle(getTitle());
+    }
+
+    public String getTitle() {
+        return mContext.getString(areStylesAvailable()
+                ? R.string.style_and_wallpaper_settings_title : R.string.wallpaper_settings_title);
+    }
+
+    public ComponentName getComponentName() {
+        return new ComponentName(mWallpaperPackage, getComponentClassString());
+    }
+
+    public String getComponentClassString() {
+        return areStylesAvailable() ? mStylesAndWallpaperClass : mWallpaperClass;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        if ((TextUtils.isEmpty(mWallpaperClass) && TextUtils.isEmpty(mStylesAndWallpaperClass))
+                || TextUtils.isEmpty(mWallpaperPackage)) {
+            Log.e(TAG, "No Wallpaper picker specified!");
+            return UNSUPPORTED_ON_DEVICE;
+        }
+        return canResolveWallpaperComponent(getComponentClassString())
+                ? AVAILABLE_UNSEARCHABLE : CONDITIONALLY_UNAVAILABLE;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        disablePreferenceIfManaged((RestrictedTopLevelPreference) preference);
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (getPreferenceKey().equals(preference.getKey())) {
+            final Intent intent = new Intent().setComponent(
+                    getComponentName()).putExtra(mWallpaperLaunchExtra, LAUNCHED_SETTINGS);
+            if (areStylesAvailable()) {
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            }
+            preference.getContext().startActivity(intent);
+            return true;
+        }
+        return super.handlePreferenceTreeClick(preference);
+    }
+
+    /** Returns whether Styles & Wallpaper is enabled and available. */
+    public boolean areStylesAvailable() {
+        return !TextUtils.isEmpty(mStylesAndWallpaperClass)
+                && canResolveWallpaperComponent(mStylesAndWallpaperClass);
+    }
+
+    private boolean canResolveWallpaperComponent(String className) {
+        final ComponentName componentName = new ComponentName(mWallpaperPackage, className);
+        final PackageManager pm = mContext.getPackageManager();
+        final Intent intent = new Intent().setComponent(componentName);
+        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0 /* flags */);
+        return resolveInfos != null && !resolveInfos.isEmpty();
+    }
+
+    private void disablePreferenceIfManaged(RestrictedTopLevelPreference pref) {
+        final String restriction = DISALLOW_SET_WALLPAPER;
+        if (pref != null) {
+            pref.setDisabledByAdmin(null);
+            if (RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
+                    restriction, UserHandle.myUserId())) {
+                // Do not show the admin dialog for system restriction.
+                pref.setEnabled(false);
+            } else {
+                pref.checkRestrictionAndSetDisabled(restriction);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 237318f..5eb9d94 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -21,6 +21,7 @@
 import android.app.settings.SettingsEnums;
 import android.os.Bundle;
 import android.util.FeatureFlagUtils;
+import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -41,6 +42,9 @@
 
 public class SettingsHomepageActivity extends FragmentActivity {
 
+    private static final String TAG = "SettingsHomepageActivity";
+    private int mSearchBoxHeight;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -50,7 +54,9 @@
         root.setSystemUiVisibility(
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
-        setHomepageContainerPaddingTop();
+        final View appBar = findViewById(R.id.app_bar_container);
+        appBar.setMinimumHeight(getSearchBoxHeight());
+        setDefaultHomepageContainerPaddingTop();
 
         final Toolbar toolbar = findViewById(R.id.search_action_bar);
         FeatureFactory.getFactory(this).getSearchFeatureProvider()
@@ -60,16 +66,36 @@
         getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
         getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
 
-        if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)
-                && !getSystemService(ActivityManager.class).isLowRamDevice()) {
-            // Only allow contextual feature on high ram devices.
-            showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
+        if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
+            // Only allow contextual features on high ram devices.
+            if (FeatureFlagUtils.isEnabled(this, FeatureFlags.SILKY_HOME)) {
+                showSuggestionFragment();
+            }
+            if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
+                showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
+            }
         }
         showFragment(new TopLevelSettings(), R.id.main_content);
         ((FrameLayout) findViewById(R.id.main_content))
                 .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
     }
 
+    private void showSuggestionFragment() {
+        final Class<? extends Fragment> fragment = FeatureFactory.getFactory(this)
+                .getSuggestionFeatureProvider(this).getContextualSuggestionFragment();
+        if (fragment == null) {
+            return;
+        }
+
+        try {
+            showFragment(fragment.newInstance(), R.id.contextual_suggestion_content);
+            setHomepageContainerTopOffset(getResources()
+                    .getDimensionPixelSize(R.dimen.suggestion_height));
+        } catch (IllegalAccessException | InstantiationException e) {
+            Log.w(TAG, "Cannot show fragment", e);
+        }
+    }
+
     private void showFragment(Fragment fragment, int id) {
         final FragmentManager fragmentManager = getSupportFragmentManager();
         final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
@@ -84,18 +110,32 @@
     }
 
     @VisibleForTesting
-    void setHomepageContainerPaddingTop() {
-        final View view = this.findViewById(R.id.homepage_container);
-
-        final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height);
-        final int searchBarMargin = getResources().getDimensionPixelSize(R.dimen.search_bar_margin);
-
-        // The top padding is the height of action bar(48dp) + top/bottom margins(16dp)
-        final int paddingTop = searchBarHeight + searchBarMargin * 2;
+    void setHomepageContainerTopOffset(int offset) {
+        final View view = findViewById(R.id.homepage_container);
+        final int paddingTop = getSearchBoxHeight() + offset;
         view.setPadding(0 /* left */, paddingTop, 0 /* right */, 0 /* bottom */);
 
         // Prevent inner RecyclerView gets focus and invokes scrolling.
         view.setFocusableInTouchMode(true);
         view.requestFocus();
     }
+
+    @VisibleForTesting
+    void setDefaultHomepageContainerPaddingTop() {
+        setHomepageContainerTopOffset(0);
+    }
+
+    @VisibleForTesting
+    int getSearchBoxHeight() {
+        if (mSearchBoxHeight != 0) {
+            return mSearchBoxHeight;
+        }
+
+        final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height);
+        final int searchBarMargin = getResources().getDimensionPixelSize(R.dimen.search_bar_margin);
+
+        // The height of search box is the height of search bar(48dp) + top/bottom margins(24dp)
+        mSearchBoxHeight = searchBarHeight + searchBarMargin * 2;
+        return mSearchBoxHeight;
+    }
 }
diff --git a/src/com/android/settings/network/MobilePlanPreferenceController.java b/src/com/android/settings/network/MobilePlanPreferenceController.java
index 255dfcd..b4135b8 100644
--- a/src/com/android/settings/network/MobilePlanPreferenceController.java
+++ b/src/com/android/settings/network/MobilePlanPreferenceController.java
@@ -145,7 +145,7 @@
             }
 
             // Get provisioning URL
-            String url = mCm.getMobileProvisioningUrl();
+            String url = mTm.getMobileProvisioningUrl();
             if (!TextUtils.isEmpty(url)) {
                 Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
                         Intent.CATEGORY_APP_BROWSER);
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
index f6908c9..5314d7f 100644
--- a/src/com/android/settings/network/ProviderModelSlice.java
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -165,16 +165,28 @@
         if (subscriptionManager == null) {
             return;
         }
-        final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
-                mHelper.isMobileDataEnabled());
         final int defaultSubId = subscriptionManager.getDefaultDataSubscriptionId();
         log("defaultSubId:" + defaultSubId);
         if (!SubscriptionManager.isUsableSubscriptionId(defaultSubId)) {
             return; // No subscription - do nothing.
         }
+        boolean requestConnectCarrier = !intent.hasExtra(EXTRA_TOGGLE_STATE);
+        // Enable the mobile data always if the user requests to connect to the carrier network.
+        boolean newState = requestConnectCarrier ? true
+                : intent.getBooleanExtra(EXTRA_TOGGLE_STATE, mHelper.isMobileDataEnabled());
 
         MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
                 false /* disableOtherSubscriptions */);
+
+        final NetworkProviderWorker worker = getWorker();
+        if (worker == null) {
+            return;
+        }
+        if (requestConnectCarrier) {
+            worker.connectCarrierNetwork();
+        } else {
+            worker.setCarrierNetworkEnabled(newState);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index 482695a..de43737 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -131,14 +131,16 @@
             e.printStackTrace();
         }
         final IconCompat levelIcon = Utils.createIconWithDrawable(drawable);
-        final PendingIntent toggleAction = mSliceable.getBroadcastIntent(mContext);
-        final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+        final PendingIntent rowIntent = mSliceable.getBroadcastIntent(mContext);
+        final SliceAction primaryAction = SliceAction.create(rowIntent,
+                levelIcon, ListBuilder.ICON_IMAGE, title);
+        final SliceAction toggleAction = SliceAction.createToggle(rowIntent,
                 "mobile_toggle" /* actionTitle */, isMobileDataEnabled());
         final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
                 .setTitle(title)
                 .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
-                .addEndItem(toggleSliceAction)
-                .setPrimaryAction(toggleSliceAction)
+                .addEndItem(toggleAction)
+                .setPrimaryAction(primaryAction)
                 .setSubtitle(summary);
         return rowBuilder;
     }
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index f4ffd09..a17fc60 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -21,10 +21,13 @@
 
 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
 import android.provider.Settings;
+import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -46,6 +49,7 @@
 import com.android.settings.network.telephony.MobileNetworkActivity;
 import com.android.settings.network.telephony.MobileNetworkUtils;
 import com.android.settings.network.telephony.SignalStrengthListener;
+import com.android.settings.widget.GearPreference;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.net.SignalStrengthUtil;
 
@@ -55,9 +59,15 @@
 import java.util.Set;
 
 /**
- * This manages a set of Preferences it places into a PreferenceGroup owned by some parent
+ * If the provider model is not enabled, this controller manages a set of Preferences it places into
+ * a PreferenceGroup owned by some parent
  * controller class - one for each available subscription. This controller is only considered
  * available if there are 2 or more subscriptions.
+ *
+ * If the provider model is enabled, this controller manages preference with data subscription
+ * information and make its state display on preference.
+ * TODO this class will clean up the multiple subscriptions functionality after the provider
+ * model is released.
  */
 public class SubscriptionsPreferenceController extends AbstractPreferenceController implements
         LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient,
@@ -68,16 +78,30 @@
     private UpdateListener mUpdateListener;
     private String mPreferenceGroupKey;
     private PreferenceGroup mPreferenceGroup;
-    private SubscriptionManager mManager;
+    private TelephonyManager mTelephonyManager;
+    private SubscriptionManager mSubscriptionManager;
     private SubscriptionsChangeListener mSubscriptionsListener;
     private MobileDataEnabledListener mDataEnabledListener;
     private DataConnectivityListener mConnectivityListener;
     private SignalStrengthListener mSignalStrengthListener;
 
+    @VisibleForTesting
+    final BroadcastReceiver mDataSubscriptionChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+                update();
+            }
+        }
+    };
 
     // Map of subscription id to Preference
     private Map<Integer, Preference> mSubscriptionPreferences;
     private int mStartOrder;
+    private GearPreference mSubsGearPref;
+
+    private SubsPrefCtrlInjector mSubsPrefCtrlInjector;
     /**
      * 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
@@ -107,21 +131,37 @@
         mUpdateListener = updateListener;
         mPreferenceGroupKey = preferenceGroupKey;
         mStartOrder = startOrder;
-        mManager = context.getSystemService(SubscriptionManager.class);
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
         mSubscriptionPreferences = new ArrayMap<>();
         mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
         mDataEnabledListener = new MobileDataEnabledListener(context, this);
         mConnectivityListener = new DataConnectivityListener(context, this);
         mSignalStrengthListener = new SignalStrengthListener(context, this);
         lifecycle.addObserver(this);
+        mSubsPrefCtrlInjector = createSubsPrefCtrlInjector();
+    }
+
+    private void registerDataSubscriptionChangedReceiver() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        mContext.registerReceiver(mDataSubscriptionChangedReceiver, filter);
+    }
+
+    private void unRegisterDataSubscriptionChangedReceiver() {
+        if (mDataSubscriptionChangedReceiver != null) {
+            mContext.unregisterReceiver(mDataSubscriptionChangedReceiver);
+        }
+
     }
 
     @OnLifecycleEvent(ON_RESUME)
     public void onResume() {
         mSubscriptionsListener.start();
-        mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
+        mDataEnabledListener.start(mSubsPrefCtrlInjector.getDefaultDataSubscriptionId());
         mConnectivityListener.start();
         mSignalStrengthListener.resume();
+        registerDataSubscriptionChangedReceiver();
         update();
     }
 
@@ -131,6 +171,7 @@
         mDataEnabledListener.stop();
         mConnectivityListener.stop();
         mSignalStrengthListener.pause();
+        unRegisterDataSubscriptionChangedReceiver();
     }
 
     @Override
@@ -143,29 +184,116 @@
         if (mPreferenceGroup == null) {
             return;
         }
-
         if (!isAvailable()) {
+            if (mSubsGearPref != null) {
+                mPreferenceGroup.removePreference(mSubsGearPref);
+            }
             for (Preference pref : mSubscriptionPreferences.values()) {
                 mPreferenceGroup.removePreference(pref);
             }
+
             mSubscriptionPreferences.clear();
             mSignalStrengthListener.updateSubscriptionIds(Collections.emptySet());
             mUpdateListener.onChildrenUpdated();
             return;
         }
 
+        if (mSubsPrefCtrlInjector.isProviderModelEnabled(mContext)) {
+            updateForProvider();
+        } else {
+            updateForBase();
+        }
+    }
+
+    private void updateForProvider() {
+        SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+        if (subInfo == null) {
+            mPreferenceGroup.removeAll();
+            return;
+        }
+        if (mSubsGearPref == null) {
+            mPreferenceGroup.removeAll();
+            mSubsGearPref = new GearPreference(mContext, null);
+            mSubsGearPref.setOnPreferenceClickListener(preference -> {
+                //TODO(b/176141379) Wait for wifiManager#selectCarrier(int subscriptionId)
+                return true;
+            });
+            mSubsGearPref.setOnGearClickListener(p ->
+                    startMobileNetworkActivity(mContext, subInfo.getSubscriptionId()));
+        }
+
+        mSubsGearPref.setTitle(subInfo.getDisplayName());
+        mSubsGearPref.setOrder(mStartOrder);
+        //TODO(b/176141828) Wait for api provided by system ui.
+        mSubsGearPref.setSummary(getMobilePreferenceSummary());
+        mSubsGearPref.setIcon(getIcon(subInfo.getSubscriptionId()));
+        mPreferenceGroup.addPreference(mSubsGearPref);
+
+        final Set<Integer> activeDataSubIds = new ArraySet<>();
+        activeDataSubIds.add(subInfo.getSubscriptionId());
+        mSignalStrengthListener.updateSubscriptionIds(activeDataSubIds);
+        mUpdateListener.onChildrenUpdated();
+    }
+
+    private String getMobilePreferenceSummary() {
+        //TODO(b/176141828) Waiting for the api provided by system UI.
+        String result = "5G";
+        if (MobileNetworkUtils.activeNetworkIsCellular(mContext)) {
+            result = "Active, " + result;
+        }
+        return result;
+    }
+
+    private Drawable getIcon(int subId) {
+        final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId);
+        final SignalStrength strength = tmForSubId.getSignalStrength();
+        int level = (strength == null) ? 0 : strength.getLevel();
+
+        int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+        if (shouldInflateSignalStrength(subId)) {
+            level += 1;
+            numLevels += 1;
+        }
+
+        final boolean isMobileDataOn = tmForSubId.isDataEnabled();
+        final boolean isActiveCellularNetwork =
+                mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext);
+        final boolean isMobileDataAccessible = tmForSubId.getDataState()
+                == TelephonyManager.DATA_CONNECTED;
+        final ServiceState serviceState = tmForSubId.getServiceState();
+        final boolean isVoiceOutOfService = (serviceState == null)
+                ? true
+                : (serviceState.getState() == ServiceState.STATE_OUT_OF_SERVICE);
+
+        Drawable icon = mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, false);
+
+        if (isActiveCellularNetwork) {
+            icon.setTint(Utils.getColorAccentDefaultColor(mContext));
+            return icon;
+        }
+        if ((isMobileDataOn && isMobileDataAccessible)
+                || (!isMobileDataOn && !isVoiceOutOfService)) {
+            return icon;
+        }
+
+        icon = mContext.getDrawable(R.drawable.ic_signal_strength_zero_bar_no_internet);
+        return icon;
+    }
+
+    private void updateForBase() {
         final Map<Integer, Preference> existingPrefs = mSubscriptionPreferences;
         mSubscriptionPreferences = new ArrayMap<>();
 
         int order = mStartOrder;
         final Set<Integer> activeSubIds = new ArraySet<>();
-        final int dataDefaultSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        for (SubscriptionInfo info : SubscriptionUtil.getActiveSubscriptions(mManager)) {
+        final int dataDefaultSubId = mSubsPrefCtrlInjector.getDefaultDataSubscriptionId();
+        for (SubscriptionInfo info :
+                SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager)) {
             final int subId = info.getSubscriptionId();
             // Avoid from showing subscription(SIM)s which has been marked as hidden
             // For example, only one subscription will be shown when there're multiple
             // subscriptions with same group UUID.
-            if (!canSubscriptionBeDisplayed(mContext, subId)) {
+            if (!mSubsPrefCtrlInjector.canSubscriptionBeDisplayed(mContext, subId)) {
                 continue;
             }
             activeSubIds.add(subId);
@@ -181,9 +309,7 @@
             pref.setOrder(order++);
 
             pref.setOnPreferenceClickListener(clickedPref -> {
-                final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
-                intent.putExtra(Settings.EXTRA_SUB_ID, subId);
-                mContext.startActivity(intent);
+                startMobileNetworkActivity(mContext, subId);
                 return true;
             });
 
@@ -198,6 +324,12 @@
         mUpdateListener.onChildrenUpdated();
     }
 
+    private static void startMobileNetworkActivity(Context context, int subId) {
+        final Intent intent = new Intent(context, MobileNetworkActivity.class);
+        intent.putExtra(Settings.EXTRA_SUB_ID, subId);
+        context.startActivity(intent);
+    }
+
     @VisibleForTesting
     boolean shouldInflateSignalStrength(int subId) {
         return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
@@ -214,14 +346,9 @@
             level += 1;
             numLevels += 1;
         }
-        final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled();
-        pref.setIcon(getIcon(level, numLevels, showCutOut));
-    }
 
-    @VisibleForTesting
-    Drawable getIcon(int level, int numLevels, boolean cutOut) {
-        return MobileNetworkUtils.getSignalStrengthIcon(mContext, level, numLevels,
-                NO_CELL_DATA_TYPE_ICON, cutOut);
+        final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled();
+        pref.setIcon(mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, showCutOut));
     }
 
     /**
@@ -236,8 +363,8 @@
      * If a subscription isn't the default for anything, we just say it is available.
      */
     protected String getSummary(int subId, boolean isDefaultForData) {
-        final int callsDefaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
-        final int smsDefaultSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
+        final int callsDefaultSubId = mSubsPrefCtrlInjector.getDefaultVoiceSubscriptionId();
+        final int smsDefaultSubId = mSubsPrefCtrlInjector.getDefaultSmsSubscriptionId();
 
         String line1 = null;
         if (subId == callsDefaultSubId && subId == smsDefaultSubId) {
@@ -253,7 +380,7 @@
             final TelephonyManager telMgrForSub = mContext.getSystemService(
                     TelephonyManager.class).createForSubscriptionId(subId);
             final boolean dataEnabled = telMgrForSub.isDataEnabled();
-            if (dataEnabled && MobileNetworkUtils.activeNetworkIsCellular(mContext)) {
+            if (dataEnabled && mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext)) {
                 line2 = mContext.getString(R.string.mobile_data_active);
             } else if (!dataEnabled) {
                 line2 = mContext.getString(R.string.mobile_data_off);
@@ -274,14 +401,16 @@
     }
 
     /**
-     * @return true if there are at least 2 available subscriptions.
+     * @return true if there are at least 2 available subscriptions,
+     * or if there is at least 1 available subscription for provider model.
      */
     @Override
     public boolean isAvailable() {
         if (mSubscriptionsListener.isAirplaneModeOn()) {
             return false;
         }
-        List<SubscriptionInfo> subInfoList = SubscriptionUtil.getActiveSubscriptions(mManager);
+        List<SubscriptionInfo> subInfoList =
+                SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
         if (subInfoList == null) {
             return false;
         }
@@ -290,8 +419,9 @@
                 // For example, only one subscription will be shown when there're multiple
                 // subscriptions with same group UUID.
                 .filter(subInfo ->
-                        canSubscriptionBeDisplayed(mContext, subInfo.getSubscriptionId()))
-                .count() >= (Utils.isProviderModelEnabled(mContext) ? 1 : 2);
+                        mSubsPrefCtrlInjector.canSubscriptionBeDisplayed(mContext,
+                                subInfo.getSubscriptionId()))
+                .count() >= (mSubsPrefCtrlInjector.isProviderModelEnabled(mContext) ? 1 : 2);
     }
 
     @Override
@@ -307,7 +437,7 @@
     @Override
     public void onSubscriptionsChanged() {
         // See if we need to change which sub id we're using to listen for enabled/disabled changes.
-        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        int defaultDataSubId = mSubsPrefCtrlInjector.getDefaultDataSubscriptionId();
         if (defaultDataSubId != mDataEnabledListener.getSubId()) {
             mDataEnabledListener.stop();
             mDataEnabledListener.start(defaultDataSubId);
@@ -335,4 +465,65 @@
         return (SubscriptionUtil.getAvailableSubscription(context,
                 ProxySubscriptionManager.getInstance(context), subId) != null);
     }
-}
+
+    SubsPrefCtrlInjector createSubsPrefCtrlInjector() {
+        return new SubsPrefCtrlInjector();
+    }
+
+    /**
+     * To inject necessary data from each static api.
+     */
+    @VisibleForTesting
+    public static class SubsPrefCtrlInjector {
+        /**
+         * Use to inject function and value for class and test class.
+         */
+        public boolean canSubscriptionBeDisplayed(Context context, int subId) {
+            return (SubscriptionUtil.getAvailableSubscription(context,
+                    ProxySubscriptionManager.getInstance(context), subId) != null);
+        }
+
+        /**
+         * Check SIM be able to display on UI.
+         */
+        public int getDefaultSmsSubscriptionId() {
+            return SubscriptionManager.getDefaultSmsSubscriptionId();
+        }
+
+        /**
+         * Get default voice subscription ID.
+         */
+        public int getDefaultVoiceSubscriptionId() {
+            return SubscriptionManager.getDefaultVoiceSubscriptionId();
+        }
+
+        /**
+         * Get default data subscription ID.
+         */
+        public int getDefaultDataSubscriptionId() {
+            return SubscriptionManager.getDefaultDataSubscriptionId();
+        }
+
+        /**
+         * Confirm the current network is cellular and active.
+         */
+        public boolean isActiveCellularNetwork(Context context) {
+            return MobileNetworkUtils.activeNetworkIsCellular(context);
+        }
+
+        /**
+         * Confirm the flag of Provider Model switch is turned on or not.
+         */
+        public boolean isProviderModelEnabled(Context context) {
+            return Utils.isProviderModelEnabled(context);
+        }
+
+        /**
+         * Get signal icon with different signal level.
+         */
+        public Drawable getIcon(Context context, int level, int numLevels, boolean cutOut) {
+            return MobileNetworkUtils.getSignalStrengthIcon(context, level, numLevels,
+                    NO_CELL_DATA_TYPE_ICON, cutOut);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/zen/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/zen/AbstractZenModePreferenceController.java
index 6fa446b..02e4015 100644
--- a/src/com/android/settings/notification/zen/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/zen/AbstractZenModePreferenceController.java
@@ -84,7 +84,10 @@
         mScreen = screen;
         Preference pref = screen.findPreference(KEY);
         if (pref != null) {
-            mSettingObserver = new SettingObserver(pref);
+            if (mSettingObserver == null) {
+                mSettingObserver = new SettingObserver();
+            }
+            mSettingObserver.setPreference(pref);
         }
     }
 
@@ -128,10 +131,13 @@
         private final Uri ZEN_MODE_DURATION_URI = Settings.Secure.getUriFor(
                 Settings.Secure.ZEN_DURATION);
 
-        private final Preference mPreference;
+        private Preference mPreference;
 
-        public SettingObserver(Preference preference) {
+        public SettingObserver() {
             super(new Handler());
+        }
+
+        public void setPreference(Preference preference) {
             mPreference = preference;
         }
 
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 81ca183..ff313c7 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -63,18 +63,6 @@
     }
 
     @Override
-    public int getInitialExpandedChildCount() {
-        int tileLimit = 1;
-        if (mWifiWakeupPreferenceController.isAvailable()) {
-            tileLimit++;
-        }
-        if (mUseOpenWifiPreferenceController.isAvailable()) {
-            tileLimit++;
-        }
-        return tileLimit;
-    }
-
-    @Override
     protected int getPreferenceScreenResId() {
         return R.xml.wifi_configure_settings;
     }
diff --git a/src/com/android/settings/wifi/WifiUtils.java b/src/com/android/settings/wifi/WifiUtils.java
index 06f74d5..d05260a 100644
--- a/src/com/android/settings/wifi/WifiUtils.java
+++ b/src/com/android/settings/wifi/WifiUtils.java
@@ -56,12 +56,12 @@
     }
 
     /**
-     * Check if the WPA2-PSK hotspot password is valid.
+     * Check if the hotspot password is valid.
      */
-    public static boolean isHotspotWpa2PasswordValid(String password) {
+    public static boolean isHotspotPasswordValid(String password, int securityType) {
         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
         try {
-            configBuilder.setPassphrase(password, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+            configBuilder.setPassphrase(password, securityType);
         } catch (IllegalArgumentException e) {
             return false;
         }
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 2957e1f..abf5bec 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -301,7 +301,11 @@
 
         final String ssid = removeFirstAndLastDoubleQuotes(softApConfiguration.getSsid());
         String security;
-        if (softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
+        final int securityType = softApConfiguration.getSecurityType();
+        if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) {
+            security = WifiQrCode.SECURITY_SAE;
+        } else if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) {
             security = WifiQrCode.SECURITY_WPA_PSK;
         } else {
             security = WifiQrCode.SECURITY_NO_PASSWORD;
@@ -431,11 +435,11 @@
 
     private static boolean isSupportHotspotConfiguratorQrCodeGenerator(
             SoftApConfiguration softApConfiguration) {
-        // QR code generator produces QR code with ZXing's Wi-Fi network config format,
-        // it supports PSK and WEP and non security
-        // KeyMgmt.NONE is for WEP or non security
-        return softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
-                || softApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OPEN;
+        final int securityType = softApConfiguration.getSecurityType();
+        return securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
+                || securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
+                || securityType == SoftApConfiguration.SECURITY_TYPE_OPEN;
     }
 
     private static boolean isSupportWifiDpp(Context context, int wifiEntrySecurity) {
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index 6c0f4aa..a87b36a 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -36,6 +36,7 @@
 
 import com.android.settings.slices.SliceBackgroundWorker;
 import com.android.settingslib.utils.ThreadUtils;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiEntry.WifiEntryCallback;
 import com.android.wifitrackerlib.WifiPickerTracker;
@@ -199,4 +200,18 @@
         }
         super.updateResults(resultList);
     }
+
+    public void setCarrierNetworkEnabled(boolean enable) {
+        final MergedCarrierEntry mergedCarrierEntry = mWifiPickerTracker.getMergedCarrierEntry();
+        if (mergedCarrierEntry != null) {
+            mergedCarrierEntry.setEnabled(enable);
+        }
+    }
+
+    public void connectCarrierNetwork() {
+        final MergedCarrierEntry mergedCarrierEntry = mWifiPickerTracker.getMergedCarrierEntry();
+        if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
+            mergedCarrierEntry.connect(null /* ConnectCallback */);
+        }
+    }
 }
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
index be67d22..287e971 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceController.java
@@ -43,6 +43,7 @@
     private static final String PREF_KEY = "wifi_tether_network_password";
 
     private String mPassword;
+    private int mSecurityType;
 
     private final MetricsFeatureProvider mMetricsFeatureProvider;
 
@@ -68,13 +69,13 @@
     @Override
     public void updateDisplay() {
         final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
-        if (config == null
-                || (config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
-                && TextUtils.isEmpty(config.getPassphrase()))) {
+        if (config.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN
+                && TextUtils.isEmpty(config.getPassphrase())) {
             mPassword = generateRandomPassword();
         } else {
             mPassword = config.getPassphrase();
         }
+        mSecurityType = config.getSecurityType();
         ((ValidatedEditTextPreference) mPreference).setValidator(this);
         ((ValidatedEditTextPreference) mPreference).setIsPassword(true);
         ((ValidatedEditTextPreference) mPreference).setIsSummaryPassword(true);
@@ -105,20 +106,21 @@
         // don't actually overwrite unless we get a new config in case it was accidentally toggled.
         if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) {
             return "";
-        } else if (!isTextValid(mPassword)) {
+        } else if (!WifiUtils.isHotspotPasswordValid(mPassword, securityType)) {
             mPassword = generateRandomPassword();
             updatePasswordDisplay((EditTextPreference) mPreference);
         }
         return mPassword;
     }
 
-    public void updateVisibility(int securityType) {
+    public void setSecurityType(int securityType) {
+        mSecurityType = securityType;
         mPreference.setVisible(securityType != SoftApConfiguration.SECURITY_TYPE_OPEN);
     }
 
     @Override
     public boolean isTextValid(String value) {
-        return WifiUtils.isHotspotWpa2PasswordValid(value);
+        return WifiUtils.isHotspotPasswordValid(value, mSecurityType);
     }
 
     private static String generateRandomPassword() {
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
index 56b5031..ec5e6e0 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceController.java
@@ -1,28 +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.wifi.tether;
 
 import static com.android.settings.AllInOneTetherSettings.DEDUP_POSTFIX;
 
+import android.annotation.NonNull;
 import android.content.Context;
+import android.net.wifi.SoftApCapability;
 import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
 import android.util.FeatureFlagUtils;
+import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.ListPreference;
 import androidx.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settings.core.FeatureFlags;
 
-public class WifiTetherSecurityPreferenceController extends WifiTetherBasePreferenceController {
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class WifiTetherSecurityPreferenceController extends WifiTetherBasePreferenceController
+    implements WifiManager.SoftApCallback {
 
     private static final String PREF_KEY = "wifi_tether_security";
 
-    private final String[] mSecurityEntries;
+    private Map<Integer, String> mSecurityMap = new LinkedHashMap<Integer, String>();
     private int mSecurityValue;
+    @VisibleForTesting
+    boolean mIsWpa3Supported = true;
 
     public WifiTetherSecurityPreferenceController(Context context,
             OnTetherConfigUpdateListener listener) {
         super(context, listener);
-        mSecurityEntries = mContext.getResources().getStringArray(R.array.wifi_tether_security);
+        final String[] securityNames = mContext.getResources().getStringArray(
+                R.array.wifi_tether_security);
+        final String[] securityValues = mContext.getResources().getStringArray(
+                R.array.wifi_tether_security_values);
+        for (int i = 0; i < securityNames.length; i++) {
+            mSecurityMap.put(Integer.parseInt(securityValues[i]), securityNames[i]);
+        }
+        mWifiManager.registerSoftApCallback(context.getMainExecutor(), this);
     }
 
     @Override
@@ -33,35 +67,48 @@
 
     @Override
     public void updateDisplay() {
-        final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
-        if (config != null && config.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_OPEN) {
-            mSecurityValue = SoftApConfiguration.SECURITY_TYPE_OPEN;
-        } else {
-            mSecurityValue = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+        final ListPreference preference = (ListPreference) mPreference;
+        // If the device is not support WPA3 then remove the WPA3 options.
+        if (!mIsWpa3Supported && mSecurityMap.keySet()
+                .removeIf(key -> key > SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)) {
+            preference.setEntries(mSecurityMap.values().stream().toArray(CharSequence[]::new));
+            preference.setEntryValues(mSecurityMap.keySet().stream().map(Integer::toBinaryString)
+                    .toArray(CharSequence[]::new));
         }
 
-        final ListPreference preference = (ListPreference) mPreference;
-        preference.setSummary(getSummaryForSecurityType(mSecurityValue));
+        final int securityType = mWifiManager.getSoftApConfiguration().getSecurityType();
+        mSecurityValue = mSecurityMap.get(securityType) != null
+                ? securityType : SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+
+        preference.setSummary(mSecurityMap.get(mSecurityValue));
         preference.setValue(String.valueOf(mSecurityValue));
     }
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         mSecurityValue = Integer.parseInt((String) newValue);
-        preference.setSummary(getSummaryForSecurityType(mSecurityValue));
-        mListener.onTetherConfigUpdated(this);
+        preference.setSummary(mSecurityMap.get(mSecurityValue));
+        if (mListener != null) {
+            mListener.onTetherConfigUpdated(this);
+        }
         return true;
     }
 
+    @Override
+    public void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {
+        final boolean isWpa3Supported =
+                softApCapability.areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_WPA3_SAE);
+        if (!isWpa3Supported) {
+            Log.i(PREF_KEY, "WPA3 SAE is not supported on this device");
+        }
+        if (mIsWpa3Supported != isWpa3Supported) {
+            mIsWpa3Supported = isWpa3Supported;
+            updateDisplay();
+        }
+        mWifiManager.unregisterSoftApCallback(this);
+    }
+
     public int getSecurityType() {
         return mSecurityValue;
     }
-
-    private String getSummaryForSecurityType(int securityType) {
-        if (securityType == SoftApConfiguration.SECURITY_TYPE_OPEN) {
-            return mSecurityEntries[1];
-        }
-        // WPA2 PSK
-        return mSecurityEntries[0];
-    }
 }
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index d6c49bc..f260298 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -192,7 +192,7 @@
     @Override
     public void onTetherConfigUpdated(AbstractPreferenceController context) {
         final SoftApConfiguration config = buildNewConfig();
-        mPasswordPreferenceController.updateVisibility(config.getSecurityType());
+        mPasswordPreferenceController.setSecurityType(config.getSecurityType());
 
         /**
          * if soft AP is stopped, bring up
@@ -216,10 +216,10 @@
         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
         final int securityType = mSecurityPreferenceController.getSecurityType();
         configBuilder.setSsid(mSSIDPreferenceController.getSSID());
-        if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
+        if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) {
             configBuilder.setPassphrase(
                     mPasswordPreferenceController.getPasswordValidated(securityType),
-                    SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+                    securityType);
         }
         configBuilder.setBand(mApBandPreferenceController.getBandIndex());
         return configBuilder.build();
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleAutoclickPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleAutoclickPreferenceControllerTest.java
new file mode 100644
index 0000000..edc2992
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleAutoclickPreferenceControllerTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.accessibility;
+
+import static android.content.Context.MODE_PRIVATE;
+
+import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.AUTOCLICK_CUSTOM_MODE;
+import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.AUTOCLICK_OFF_MODE;
+import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.KEY_AUTOCLICK_CUSTOM_SEEKBAR;
+import static com.android.settings.accessibility.ToggleAutoclickPreferenceController.KEY_DELAY_MODE;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.provider.Settings.Secure;
+
+import androidx.lifecycle.LifecycleObserver;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.accessibility.ToggleAutoclickPreferenceController.OnChangeListener;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.widget.RadioButtonPreference;
+import com.android.settingslib.widget.RadioButtonPreference.OnClickListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Map;
+
+/** Tests for {@link ToggleAutoclickPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class ToggleAutoclickPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+
+    @Mock
+    private RadioButtonPreference mDelayModePref;
+
+    @Mock
+    private OnChangeListener mOnChangeListener;
+
+    @Mock
+    private LayoutPreference mSeekBarPref;
+
+    @Mock
+    private Map<String, Integer> mAccessibilityAutoclickKeyToValueMap;
+
+    private ToggleAutoclickPreferenceController mController;
+    private SharedPreferences mSharedPreferences;
+    private final String mPrefKey = "prefKey";
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mController = new ToggleAutoclickPreferenceController(mContext, mPrefKey);
+        mController.mAccessibilityAutoclickKeyToValueMap = mAccessibilityAutoclickKeyToValueMap;
+        mSharedPreferences =
+                mContext.getSharedPreferences(mContext.getPackageName(), MODE_PRIVATE);
+
+        when(mScreen.findPreference(mPrefKey)).thenReturn(mDelayModePref);
+        when(mScreen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR)).thenReturn(mSeekBarPref);
+        when(mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey())).thenReturn(
+                AUTOCLICK_OFF_MODE);
+    }
+
+    @Test
+    public void getAvailabilityStatus_available() {
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void setClickListenerOnDelayModePref_whenDisplay_success() {
+        mController.displayPreference(mScreen);
+
+        verify(mDelayModePref).setOnClickListener(any(OnClickListener.class));
+    }
+
+    @Test
+    public void constructor_hasLifecycle_addObserver() {
+        final Lifecycle lifecycle = mock(Lifecycle.class);
+        mController = new ToggleAutoclickPreferenceController(mContext, lifecycle, mPrefKey);
+
+        verify(lifecycle).addObserver(any(LifecycleObserver.class));
+    }
+
+    @Test
+    public void onRadioButtonClicked_offMode_disableAutoClick() {
+        when(mAccessibilityAutoclickKeyToValueMap.get(mPrefKey)).thenReturn(AUTOCLICK_OFF_MODE);
+
+        mController.displayPreference(mScreen);
+        mController.onRadioButtonClicked(any(RadioButtonPreference.class));
+        final boolean isEnabled = Secure.getInt(mContext.getContentResolver(),
+                Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, /* def= */ 0) == 1;
+        final int delayMs = Secure.getInt(mContext.getContentResolver(),
+                Secure.ACCESSIBILITY_AUTOCLICK_DELAY, /* def= */ 0);
+        final int keyDelayMode = mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE);
+
+        assertThat(keyDelayMode).isEqualTo(AUTOCLICK_OFF_MODE);
+        assertThat(delayMs).isEqualTo(/* expected= */ 0);
+        assertThat(isEnabled).isFalse();
+    }
+
+    @Test
+    public void onRadioButtonClicked_customMode_enableAutoClick() {
+        when(mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey())).thenReturn(
+                AUTOCLICK_CUSTOM_MODE);
+        when(mAccessibilityAutoclickKeyToValueMap.get(mPrefKey)).thenReturn(AUTOCLICK_CUSTOM_MODE);
+
+        mController.displayPreference(mScreen);
+        mController.onRadioButtonClicked(any(RadioButtonPreference.class));
+        final boolean isEnabled = Secure.getInt(mContext.getContentResolver(),
+                Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, /* def= */ 0) == 1;
+        final int keyDelayMode = mSharedPreferences.getInt(KEY_DELAY_MODE, AUTOCLICK_CUSTOM_MODE);
+
+        assertThat(keyDelayMode).isEqualTo(AUTOCLICK_CUSTOM_MODE);
+        assertThat(isEnabled).isTrue();
+    }
+
+    @Test
+    public void onRadioButtonClicked_hasListener_runOnCheckedChanged() {
+        when(mAccessibilityAutoclickKeyToValueMap.get(mDelayModePref.getKey())).thenReturn(
+                AUTOCLICK_CUSTOM_MODE);
+        when(mAccessibilityAutoclickKeyToValueMap.get(mPrefKey)).thenReturn(AUTOCLICK_CUSTOM_MODE);
+
+        mController.setOnChangeListener(mOnChangeListener);
+        mController.displayPreference(mScreen);
+        mController.onRadioButtonClicked(any(RadioButtonPreference.class));
+
+        verify(mOnChangeListener).onCheckedChanged(mDelayModePref);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java b/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.java
new file mode 100644
index 0000000..57df456
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/VideoPlayerTest.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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.view.Surface;
+import android.view.TextureView;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.accessibility.VideoPlayer.State;
+
+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;
+
+/** Tests for {@link VideoPlayer}. */
+@RunWith(RobolectricTestRunner.class)
+public class VideoPlayerTest {
+
+    @Mock
+    private MediaPlayer mMediaPlayer;
+
+    @Mock
+    private TextureView mTextureView;
+
+    @Mock
+    private Surface mSurface;
+
+    private VideoPlayer mVideoPlayer;
+
+    @Before
+    public void initVideoPlayer() {
+        MockitoAnnotations.initMocks(this);
+
+        final int videoRes = 0;
+        final Context context = ApplicationProvider.getApplicationContext();
+
+        mVideoPlayer = spy(VideoPlayer.create(context, videoRes, mTextureView));
+        mVideoPlayer.mMediaPlayer = mMediaPlayer;
+        mVideoPlayer.mAnimationSurface = mSurface;
+    }
+
+    @Test
+    public void setSurfaceTextureListener_success() {
+        verify(mTextureView).setSurfaceTextureListener(any());
+    }
+
+    @Test
+    public void onPlayerPaused_startedState_pause() {
+        mVideoPlayer.mMediaPlayerState = State.STARTED;
+
+        mVideoPlayer.pause();
+
+        assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.PAUSED);
+        verify(mMediaPlayer).pause();
+    }
+
+    @Test
+    public void onPlayerResumed_pausedState_start() {
+        mVideoPlayer.mMediaPlayerState = State.PAUSED;
+
+        mVideoPlayer.resume();
+
+        assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.STARTED);
+        verify(mMediaPlayer).start();
+    }
+
+    @Test
+    public void onPlayerReleased_stoppedState_release() {
+        mVideoPlayer.mMediaPlayerState = State.STOPPED;
+
+        mVideoPlayer.release();
+
+        assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.END);
+        verify(mMediaPlayer).release();
+        verify(mSurface).release();
+    }
+
+    @Test
+    public void onSurfaceTextureDestroyed_preparedState_release() {
+        mVideoPlayer.mMediaPlayerState = State.PREPARED;
+
+        mVideoPlayer.onSurfaceTextureDestroyed(any());
+
+        assertThat(mVideoPlayer.mMediaPlayerState).isEqualTo(State.END);
+        verify(mMediaPlayer).release();
+        verify(mSurface).release();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java
new file mode 100644
index 0000000..7b3ae65
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowPackageManager;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {SettingsShadowResources.class})
+public class TopLevelWallpaperPreferenceControllerTest {
+    private static final String TEST_KEY = "test_key";
+
+    private Intent mWallpaperIntent;
+    private Intent mStylesAndWallpaperIntent;
+    private FragmentActivity mContext;
+    private ShadowPackageManager mShadowPackageManager;
+
+    private TopLevelWallpaperPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = Robolectric.buildActivity(FragmentActivity.class).get();
+        SettingsShadowResources.overrideResource(
+                R.string.config_wallpaper_picker_package, "bogus.package.for.testing");
+        SettingsShadowResources.overrideResource(
+                R.string.config_styles_and_wallpaper_picker_class, "bogus.package.class");
+        mWallpaperIntent = new Intent().setComponent(new ComponentName(
+                mContext.getString(R.string.config_wallpaper_picker_package),
+                mContext.getString(R.string.config_wallpaper_picker_class)));
+        mStylesAndWallpaperIntent = new Intent().setComponent(new ComponentName(
+                mContext.getString(R.string.config_wallpaper_picker_package),
+                mContext.getString(R.string.config_styles_and_wallpaper_picker_class)));
+        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+        mController = new TopLevelWallpaperPreferenceController(mContext, TEST_KEY);
+    }
+
+    @Test
+    public void isAvailable_wallpaperPickerEnabledAndStylePickerEnabled_returnsTrue() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_wallpaperPickerEnabledAndStylePickerDisabled_returnsTrue() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_wallpaperPickerDisabledAndStylePickerEnabled_returnsTrue() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList());
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_wallpaperPickerDisabledAndStylePickerDisabled_returnsFalse() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList());
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void getComponentClassString_stylesAvailable_returnsStylePickerClassString() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent,
+                Lists.newArrayList(mock(ResolveInfo.class)));
+        assertThat(mController.getComponentClassString())
+                .isEqualTo(mContext.getString(R.string.config_styles_and_wallpaper_picker_class));
+    }
+
+    @Test
+    public void getComponentClassString_stylesUnavailable_returnsWallpaperPickerClassString() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+        assertThat(mController.getComponentClassString())
+                .isEqualTo(mContext.getString(R.string.config_wallpaper_picker_class));
+    }
+
+    @Test
+    public void areStylesAvailable_noComponentSpecified() {
+        SettingsShadowResources.overrideResource(
+                R.string.config_styles_and_wallpaper_picker_class, "");
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+
+        assertThat(mController.areStylesAvailable()).isFalse();
+    }
+
+    @Test
+    public void areStylesAvailable_componentUnresolveable() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+
+        assertThat(mController.areStylesAvailable()).isFalse();
+    }
+
+    @Test
+    public void areStylesAvailable_componentResolved() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent,
+                Lists.newArrayList(mock(ResolveInfo.class)));
+
+        assertThat(mController.areStylesAvailable()).isTrue();
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_wallpaperOnly() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+        Preference preference = new Preference(mContext);
+        preference.setKey(TEST_KEY);
+
+        mController.handlePreferenceTreeClick(preference);
+
+        assertThat(Shadows.shadowOf(mContext)
+                .getNextStartedActivityForResult().intent.getComponent().getClassName())
+                .isEqualTo(mContext.getString(R.string.config_wallpaper_picker_class));
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_stylesAndWallpaper() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList());
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+        Preference preference = new Preference(mContext);
+        preference.setKey(TEST_KEY);
+
+        mController.handlePreferenceTreeClick(preference);
+
+        assertThat(Shadows.shadowOf(mContext)
+                .getNextStartedActivityForResult().intent.getComponent().getClassName())
+                .isEqualTo(mContext.getString(R.string.config_styles_and_wallpaper_picker_class));
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_launchSourceExtra() {
+        mShadowPackageManager.setResolveInfosForIntent(
+                mWallpaperIntent, Lists.newArrayList());
+        mShadowPackageManager.setResolveInfosForIntent(
+                mStylesAndWallpaperIntent, Lists.newArrayList());
+        Preference preference = new Preference(mContext);
+        preference.setKey(TEST_KEY);
+
+        mController.handlePreferenceTreeClick(preference);
+
+        assertThat(Shadows.shadowOf(mContext).getNextStartedActivityForResult()
+                .intent.hasExtra("com.android.wallpaper.LAUNCH_SOURCE")).isTrue();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index db12580..4c7b4b5 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -48,25 +48,36 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class SettingsHomepageActivityTest {
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
     }
 
     @Test
-    public void setHomepageContainerPaddingTop_shouldBeSetPaddingTop() {
+    public void setDefaultHomepageContainerPaddingTop_shouldSetSearchBoxHeight() {
         final SettingsHomepageActivity activity = Robolectric.buildActivity(
                 SettingsHomepageActivity.class).create().get();
-        final int searchBarHeight = activity.getResources().getDimensionPixelSize(
-                R.dimen.search_bar_height);
-        final int searchBarMargin = activity.getResources().getDimensionPixelSize(
-                R.dimen.search_bar_margin);
         final View view = activity.findViewById(R.id.homepage_container);
 
-        activity.setHomepageContainerPaddingTop();
+        activity.setDefaultHomepageContainerPaddingTop();
 
         final int actualPaddingTop = view.getPaddingTop();
-        assertThat(actualPaddingTop).isEqualTo(searchBarHeight + searchBarMargin * 2);
+        assertThat(actualPaddingTop).isEqualTo(activity.getSearchBoxHeight());
+    }
+
+    @Test
+    public void setHomepageContainerTopOffset_shouldBeSetPaddingTop() {
+        final SettingsHomepageActivity activity = Robolectric.buildActivity(
+                SettingsHomepageActivity.class).create().get();
+        final View view = activity.findViewById(R.id.homepage_container);
+        final int offset = activity.getResources().getDimensionPixelSize(
+                R.dimen.suggestion_height);
+
+        activity.setHomepageContainerTopOffset(offset);
+
+        final int actualPaddingTop = view.getPaddingTop();
+        assertThat(actualPaddingTop).isEqualTo(activity.getSearchBoxHeight() + offset);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
deleted file mode 100644
index 57b2e2f..0000000
--- a/tests/robotests/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
-import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GOOD;
-import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
-import static android.telephony.SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-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.anyInt;
-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.graphics.drawable.Drawable;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.provider.Settings;
-import android.telephony.SignalStrength;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowSubscriptionManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowSubscriptionManager.class)
-public class SubscriptionsPreferenceControllerTest {
-    private static final String KEY = "preference_group";
-
-    @Mock
-    private PreferenceScreen mScreen;
-    @Mock
-    private PreferenceCategory mPreferenceCategory;
-    @Mock
-    private SubscriptionManager mSubscriptionManager;
-    @Mock
-    private ConnectivityManager mConnectivityManager;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private Network mActiveNetwork;
-    @Mock
-    private NetworkCapabilities mCapabilities;
-    @Mock
-    private Drawable mSignalStrengthIcon;
-
-    private Context mContext;
-    private LifecycleOwner mLifecycleOwner;
-    private Lifecycle mLifecycle;
-    private SubscriptionsPreferenceController mController;
-    private int mOnChildUpdatedCount;
-    private SubscriptionsPreferenceController.UpdateListener mUpdateListener;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application);
-        mLifecycleOwner = () -> mLifecycle;
-        mLifecycle = new Lifecycle(mLifecycleOwner);
-        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
-        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
-        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
-        when(mConnectivityManager.getActiveNetwork()).thenReturn(mActiveNetwork);
-        when(mConnectivityManager.getNetworkCapabilities(mActiveNetwork)).thenReturn(mCapabilities);
-        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
-        when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory);
-        when(mPreferenceCategory.getContext()).thenReturn(mContext);
-        mOnChildUpdatedCount = 0;
-        mUpdateListener = () -> mOnChildUpdatedCount++;
-
-        mController = spy(
-                new SubscriptionsPreferenceController(mContext, mLifecycle, mUpdateListener,
-                        KEY, 5));
-        doReturn(true).when(mController).canSubscriptionBeDisplayed(any(), anyInt());
-        doReturn(mSignalStrengthIcon).when(mController).getIcon(anyInt(), anyInt(), anyBoolean());
-    }
-
-    @After
-    public void tearDown() {
-        SubscriptionUtil.setActiveSubscriptionsForTesting(null);
-    }
-
-    @Test
-    public void isAvailable_oneSubscription_availableFalse() {
-        setupMockSubscriptions(1);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_twoSubscriptions_availableTrue() {
-        setupMockSubscriptions(2);
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void isAvailable_fiveSubscriptions_availableTrue() {
-        setupMockSubscriptions(5);
-        assertThat(mController.isAvailable()).isTrue();
-    }
-
-    @Test
-    public void isAvailable_airplaneModeOn_availableFalse() {
-        setupMockSubscriptions(2);
-        assertThat(mController.isAvailable()).isTrue();
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void onAirplaneModeChanged_airplaneModeTurnedOn_eventFired() {
-        setupMockSubscriptions(2);
-        mController.onResume();
-        mController.displayPreference(mScreen);
-        assertThat(mController.isAvailable()).isTrue();
-
-        final int updateCountBeforeModeChange = mOnChildUpdatedCount;
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
-        mController.onAirplaneModeChanged(true);
-        assertThat(mController.isAvailable()).isFalse();
-        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
-    }
-
-    @Test
-    public void onAirplaneModeChanged_airplaneModeTurnedOff_eventFired() {
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
-        setupMockSubscriptions(2);
-        mController.onResume();
-        mController.displayPreference(mScreen);
-        assertThat(mController.isAvailable()).isFalse();
-
-        final int updateCountBeforeModeChange = mOnChildUpdatedCount;
-        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
-        mController.onAirplaneModeChanged(false);
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
-    }
-
-    @Test
-    public void onSubscriptionsChanged_countBecameTwo_eventFired() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
-        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1));
-        mController.onResume();
-        mController.displayPreference(mScreen);
-        assertThat(mController.isAvailable()).isFalse();
-
-        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
-        SubscriptionUtil.setActiveSubscriptionsForTesting(subs);
-        mController.onSubscriptionsChanged();
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
-    }
-
-    @Test
-    public void onSubscriptionsChanged_countBecameOne_eventFiredAndPrefsRemoved() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
-        mController.onResume();
-        mController.displayPreference(mScreen);
-        assertThat(mController.isAvailable()).isTrue();
-        verify(mPreferenceCategory, times(2)).addPreference(any(Preference.class));
-
-        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
-        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1));
-        mController.onSubscriptionsChanged();
-        assertThat(mController.isAvailable()).isFalse();
-        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
-
-        verify(mPreferenceCategory, times(2)).removePreference(any(Preference.class));
-    }
-
-    @Test
-    public void onSubscriptionsChanged_subscriptionReplaced_preferencesChanged() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(3);
-
-        // Start out with only sub1 and sub2.
-        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 2));
-        mController.onResume();
-        mController.displayPreference(mScreen);
-        final ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
-        verify(mPreferenceCategory, times(2)).addPreference(captor.capture());
-        assertThat(captor.getAllValues().size()).isEqualTo(2);
-        assertThat(captor.getAllValues().get(0).getTitle()).isEqualTo("sub1");
-        assertThat(captor.getAllValues().get(1).getTitle()).isEqualTo("sub2");
-
-        // Now replace sub2 with sub3, and make sure the old preference was removed and the new
-        // preference was added.
-        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
-        SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(subs.get(0), subs.get(2)));
-        mController.onSubscriptionsChanged();
-        assertThat(mController.isAvailable()).isTrue();
-        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
-
-        verify(mPreferenceCategory).removePreference(captor.capture());
-        assertThat(captor.getValue().getTitle()).isEqualTo("sub2");
-        verify(mPreferenceCategory, times(3)).addPreference(captor.capture());
-        assertThat(captor.getValue().getTitle()).isEqualTo("sub3");
-    }
-
-
-    /**
-     * Helper to create a specified number of subscriptions, display them, and then click on one and
-     * verify that the intent fires and has the right subscription id extra.
-     *
-     * @param subscriptionCount the number of subscriptions
-     * @param selectedPrefIndex index of the subscription to click on
-     */
-    private void runPreferenceClickTest(final int subscriptionCount, final int selectedPrefIndex) {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(subscriptionCount);
-        final ArgumentCaptor<Preference> prefCaptor = ArgumentCaptor.forClass(Preference.class);
-        mController.displayPreference(mScreen);
-        verify(mPreferenceCategory, times(subscriptionCount)).addPreference(prefCaptor.capture());
-        final List<Preference> prefs = prefCaptor.getAllValues();
-        final Preference pref = prefs.get(selectedPrefIndex);
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        doNothing().when(mContext).startActivity(intentCaptor.capture());
-        pref.getOnPreferenceClickListener().onPreferenceClick(pref);
-        final Intent intent = intentCaptor.getValue();
-        assertThat(intent).isNotNull();
-        assertThat(intent.hasExtra(Settings.EXTRA_SUB_ID)).isTrue();
-        final int subIdFromIntent = intent.getIntExtra(Settings.EXTRA_SUB_ID,
-                INVALID_SUBSCRIPTION_ID);
-        assertThat(subIdFromIntent).isEqualTo(
-                subs.get(selectedPrefIndex).getSubscriptionId());
-    }
-
-    @Test
-    public void twoPreferences_firstPreferenceClicked_correctIntentFires() {
-        runPreferenceClickTest(2, 0);
-    }
-
-    @Test
-    public void twoPreferences_secondPreferenceClicked_correctIntentFires() {
-        runPreferenceClickTest(2, 1);
-    }
-
-    @Test
-    public void threePreferences_secondPreferenceClicked_correctIntentFires() {
-        runPreferenceClickTest(3, 1);
-    }
-
-    @Test
-    public void threePreferences_thirdPreferenceClicked_correctIntentFires() {
-        runPreferenceClickTest(3, 2);
-    }
-
-    @Test
-    public void getSummary_twoSubsOneDefaultForEverythingDataActive() {
-        setupMockSubscriptions(2);
-
-        ShadowSubscriptionManager.setDefaultSmsSubscriptionId(11);
-        ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(11);
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-        when(mCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)).thenReturn(true);
-
-        assertThat(mController.getSummary(11, true)).isEqualTo(
-                mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator()
-                        + mContext.getString(R.string.mobile_data_active));
-
-        assertThat(mController.getSummary(22, false)).isEqualTo(
-                mContext.getString(R.string.subscription_available));
-    }
-
-    @Test
-    public void getSummary_twoSubsOneDefaultForEverythingDataNotActive() {
-        setupMockSubscriptions(2, 1, true);
-
-        ShadowSubscriptionManager.setDefaultSmsSubscriptionId(1);
-        ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1);
-
-        assertThat(mController.getSummary(1, true)).isEqualTo(
-                mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator()
-                        + mContext.getString(R.string.default_for_mobile_data));
-
-        assertThat(mController.getSummary(2, false)).isEqualTo(
-                mContext.getString(R.string.subscription_available));
-    }
-
-    @Test
-    public void getSummary_twoSubsOneDefaultForEverythingDataDisabled() {
-        setupMockSubscriptions(2);
-
-        ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1);
-        ShadowSubscriptionManager.setDefaultSmsSubscriptionId(1);
-
-        assertThat(mController.getSummary(1, true)).isEqualTo(
-                mContext.getString(R.string.default_for_calls_and_sms) + System.lineSeparator()
-                        + mContext.getString(R.string.mobile_data_off));
-
-        assertThat(mController.getSummary(2, false)).isEqualTo(
-                mContext.getString(R.string.subscription_available));
-    }
-
-    @Test
-    public void getSummary_twoSubsOneForCallsAndDataOneForSms() {
-        setupMockSubscriptions(2, 1, true);
-
-        ShadowSubscriptionManager.setDefaultSmsSubscriptionId(2);
-        ShadowSubscriptionManager.setDefaultVoiceSubscriptionId(1);
-
-        assertThat(mController.getSummary(1, true)).isEqualTo(
-                mContext.getString(R.string.default_for_calls) + System.lineSeparator()
-                        + mContext.getString(R.string.default_for_mobile_data));
-
-        assertThat(mController.getSummary(2, false)).isEqualTo(
-                mContext.getString(R.string.default_for_sms));
-    }
-
-    @Test
-    public void setIcon_nullStrength_noCrash() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
-        setMockSubSignalStrength(subs, 0, -1);
-        final Preference pref = mock(Preference.class);
-
-        mController.setIcon(pref, 1, true /* isDefaultForData */);
-        verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true));
-    }
-
-    @Test
-    public void setIcon_noSignal_correctLevels() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
-        setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
-        setMockSubDataEnabled(subs, 0, true);
-        final Preference pref = mock(Preference.class);
-
-        mController.setIcon(pref, 1, true /* isDefaultForData */);
-        verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(false));
-
-        mController.setIcon(pref, 2, false /* isDefaultForData */);
-        verify(mController).getIcon(eq(0), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true));
-    }
-
-    @Test
-    public void setIcon_noSignal_withInflation_correctLevels() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
-        setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
-        final Preference pref = mock(Preference.class);
-        doReturn(true).when(mController).shouldInflateSignalStrength(anyInt());
-
-        mController.setIcon(pref, 1, true /* isDefaultForData */);
-        verify(mController).getIcon(eq(1), eq(NUM_SIGNAL_STRENGTH_BINS + 1), eq(false));
-
-        mController.setIcon(pref, 2, false /* isDefaultForData */);
-        verify(mController).getIcon(eq(1), eq(NUM_SIGNAL_STRENGTH_BINS + 1), eq(true));
-    }
-
-    @Test
-    public void setIcon_greatSignal_correctLevels() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
-        setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_GREAT);
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GREAT);
-        final Preference pref = mock(Preference.class);
-
-        mController.setIcon(pref, 1, true /* isDefaultForData */);
-        verify(mController).getIcon(eq(4), eq(NUM_SIGNAL_STRENGTH_BINS), eq(false));
-
-        mController.setIcon(pref, 2, false /* isDefaultForData */);
-        verify(mController).getIcon(eq(4), eq(NUM_SIGNAL_STRENGTH_BINS), eq(true));
-    }
-
-    @Test
-    public void onSignalStrengthChanged_subTwoGoesFromGoodToGreat_correctLevels() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
-        setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_POOR);
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GOOD);
-
-        mController.onResume();
-        mController.displayPreference(mScreen);
-
-        // Now change the signal strength for the 2nd subscription from Good to Great
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GREAT);
-        mController.onSignalStrengthChanged();
-
-        final ArgumentCaptor<Integer> level = ArgumentCaptor.forClass(Integer.class);
-        verify(mController, times(4)).getIcon(level.capture(), eq(NUM_SIGNAL_STRENGTH_BINS),
-                eq(true));
-        assertThat(level.getAllValues().get(0)).isEqualTo(1);
-        assertThat(level.getAllValues().get(1)).isEqualTo(3); // sub2, first time
-        assertThat(level.getAllValues().get(2)).isEqualTo(1);
-        assertThat(level.getAllValues().get(3)).isEqualTo(4); // sub2, after change
-    }
-
-    @Test
-    public void displayPreference_mobileDataOff_bothSubsHaveCutOut() {
-        setupMockSubscriptions(2, 1, false);
-
-        mController.onResume();
-        mController.displayPreference(mScreen);
-
-        verify(mController, times(2)).getIcon(eq(SIGNAL_STRENGTH_GOOD),
-                eq(NUM_SIGNAL_STRENGTH_BINS), eq(true));
-    }
-
-    @Test
-    public void displayPreference_mobileDataOn_onlyNonDefaultSubHasCutOut() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
-        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_POOR);
-
-        mController.onResume();
-        mController.displayPreference(mScreen);
-
-        verify(mController).getIcon(eq(SIGNAL_STRENGTH_GOOD), eq(NUM_SIGNAL_STRENGTH_BINS),
-                eq(false));
-        verify(mController).getIcon(eq(SIGNAL_STRENGTH_POOR), eq(NUM_SIGNAL_STRENGTH_BINS),
-                eq(true));
-    }
-
-    @Test
-    public void displayPreference_subscriptionsWithSameGroupUUID_onlyOneWillBeSeen() {
-        doReturn(false).when(mController).canSubscriptionBeDisplayed(any(), eq(3));
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(3);
-        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 3));
-
-        mController.onResume();
-        mController.displayPreference(mScreen);
-
-        verify(mPreferenceCategory, times(2)).addPreference(any(Preference.class));
-    }
-
-    @Test
-    public void onMobileDataEnabledChange_mobileDataTurnedOff_bothSubsHaveCutOut() {
-        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
-
-        mController.onResume();
-        mController.displayPreference(mScreen);
-
-        setMockSubDataEnabled(subs, 0, false);
-        mController.onMobileDataEnabledChange();
-
-        final ArgumentCaptor<Boolean> cutOutCaptor = ArgumentCaptor.forClass(Boolean.class);
-        verify(mController, times(4)).getIcon(eq(SIGNAL_STRENGTH_GOOD),
-                eq(NUM_SIGNAL_STRENGTH_BINS), cutOutCaptor.capture());
-        assertThat(cutOutCaptor.getAllValues().get(0)).isEqualTo(false); // sub1, first time
-        assertThat(cutOutCaptor.getAllValues().get(1)).isEqualTo(true);
-        assertThat(cutOutCaptor.getAllValues().get(2)).isEqualTo(true); // sub1, second time
-        assertThat(cutOutCaptor.getAllValues().get(3)).isEqualTo(true);
-    }
-
-    private List<SubscriptionInfo> setupMockSubscriptions(int count) {
-        return setupMockSubscriptions(count, 0, true);
-    }
-
-    /** Helper method to setup several mock active subscriptions. The generated subscription id's
-     * start at 1.
-     *
-     * @param count How many subscriptions to create
-     * @param defaultDataSubId The subscription id of the default data subscription - pass
-     *                         INVALID_SUBSCRIPTION_ID if there should not be one
-     * @param mobileDataEnabled Whether mobile data should be considered enabled for the default
-     *                          data subscription
-     */
-    private List<SubscriptionInfo> setupMockSubscriptions(int count, int defaultDataSubId,
-            boolean mobileDataEnabled) {
-        if (defaultDataSubId != INVALID_SUBSCRIPTION_ID) {
-            ShadowSubscriptionManager.setDefaultDataSubscriptionId(defaultDataSubId);
-        }
-        final ArrayList<SubscriptionInfo> infos = new ArrayList<>();
-        for (int i = 0; i < count; i++) {
-            final int subscriptionId = i + 1;
-            final SubscriptionInfo info = mock(SubscriptionInfo.class);
-            final TelephonyManager mgrForSub = mock(TelephonyManager.class);
-            final SignalStrength signalStrength = mock(SignalStrength.class);
-
-            if (subscriptionId == defaultDataSubId) {
-                when(mgrForSub.isDataEnabled()).thenReturn(mobileDataEnabled);
-            }
-            when(info.getSubscriptionId()).thenReturn(i + 1);
-            when(info.getDisplayName()).thenReturn("sub" + (i + 1));
-            doReturn(mgrForSub).when(mTelephonyManager).createForSubscriptionId(eq(subscriptionId));
-            when(mgrForSub.getSignalStrength()).thenReturn(signalStrength);
-            when(signalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GOOD);
-
-            infos.add(info);
-        }
-        SubscriptionUtil.setActiveSubscriptionsForTesting(infos);
-        return infos;
-    }
-
-    /**
-     * Helper method to set the signal strength returned for a mock subscription
-     * @param subs The list of subscriptions
-     * @param index The index in of the subscription in |subs| to change
-     * @param level The signal strength level to return for the subscription. Pass -1 to force
-     *              return of a null SignalStrength object for the subscription.
-     */
-    private void setMockSubSignalStrength(List<SubscriptionInfo> subs, int index, int level) {
-        final TelephonyManager mgrForSub =
-                mTelephonyManager.createForSubscriptionId(subs.get(index).getSubscriptionId());
-        if (level == -1) {
-            when(mgrForSub.getSignalStrength()).thenReturn(null);
-        } else {
-            final SignalStrength signalStrength = mgrForSub.getSignalStrength();
-            when(signalStrength.getLevel()).thenReturn(level);
-        }
-    }
-
-    private void setMockSubDataEnabled(List<SubscriptionInfo> subs, int index, boolean enabled) {
-        final TelephonyManager mgrForSub =
-                mTelephonyManager.createForSubscriptionId(subs.get(index).getSubscriptionId());
-        when(mgrForSub.isDataEnabled()).thenReturn(enabled);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
index 395048c..2a8d265 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiScanWorkerTest.java
@@ -20,11 +20,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import androidx.lifecycle.Lifecycle;
 
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiPickerTracker;
 
@@ -44,6 +47,8 @@
     private WifiScanWorker mWifiScanWorker;
     @Mock
     WifiPickerTracker mWifiPickerTracker;
+    @Mock
+    MergedCarrierEntry mMergedCarrierEntry;
 
     @Before
     public void setUp() {
@@ -51,6 +56,7 @@
 
         mWifiScanWorker = new WifiScanWorker(RuntimeEnvironment.application, WIFI_SLICE_URI);
         mWifiScanWorker.mWifiPickerTracker = mWifiPickerTracker;
+        when(mWifiPickerTracker.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
     }
 
     @Test
@@ -102,4 +108,24 @@
 
         assertThat(mWifiScanWorker.getWifiEntry(key)).isEqualTo(reachableWifiEntry);
     }
+
+    @Test
+    public void setCarrierNetworkEnabled_shouldCallMergedCarrierEntrySetEnabled() {
+        mWifiScanWorker.setCarrierNetworkEnabled(true);
+
+        verify(mMergedCarrierEntry).setEnabled(true);
+
+        mWifiScanWorker.setCarrierNetworkEnabled(false);
+
+        verify(mMergedCarrierEntry).setEnabled(false);
+    }
+
+    @Test
+    public void connectCarrierNetwork_shouldCallMergedCarrierEntryConnect() {
+        when(mMergedCarrierEntry.canConnect()).thenReturn(true);
+
+        mWifiScanWorker.connectCarrierNetwork();
+
+        verify(mMergedCarrierEntry).connect(any());
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
deleted file mode 100644
index c7d0695..0000000
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.android.settings.wifi.tether;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiManager;
-
-import androidx.preference.ListPreference;
-import androidx.preference.PreferenceScreen;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiTetherSecurityPreferenceControllerTest {
-
-    private static final String WPA2_PSK =
-            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-    private static final String NONE = String.valueOf(SoftApConfiguration.SECURITY_TYPE_OPEN);
-    @Mock
-    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
-    private Context mContext;
-    @Mock
-    private ConnectivityManager mConnectivityManager;
-    @Mock
-    private WifiManager mWifiManager;
-    @Mock
-    private PreferenceScreen mScreen;
-    private WifiTetherSecurityPreferenceController mController;
-    private ListPreference mPreference;
-    private SoftApConfiguration mConfig;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mConfig = new SoftApConfiguration.Builder().setSsid("test_1234")
-                .setPassphrase("test_password",
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
-        mContext = spy(RuntimeEnvironment.application);
-
-        when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(mConfig);
-        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
-                .thenReturn(mConnectivityManager);
-        when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
-        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
-
-        mController = new WifiTetherSecurityPreferenceController(mContext, mListener);
-        mPreference = new ListPreference(RuntimeEnvironment.application);
-        mController.mPreference = mPreference;
-    }
-
-    @Test
-    public void onPreferenceChange_securityValueUpdated() {
-        mController.onPreferenceChange(mPreference, WPA2_PSK);
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-
-        mController.onPreferenceChange(mPreference, NONE);
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_OPEN);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
-    }
-
-    @Test
-    public void updateDisplay_preferenceUpdated() {
-        // test defaulting to WPA2-Personal on new config
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(null);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-
-        // test open tether network
-        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
-                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_OPEN);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
-
-        // test WPA2-Personal tether network
-        SoftApConfiguration config2 = new SoftApConfiguration.Builder(mConfig)
-                .setPassphrase("test_password",
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
-        when(mWifiManager.getSoftApConfiguration()).thenReturn(config2);
-        mController.updateDisplay();
-        assertThat(mController.getSecurityType()).isEqualTo(
-                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
-        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
-    }
-}
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
index 9c16b8a..f9450db 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network;
 
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -113,13 +115,14 @@
 
         // Set-up specs for SliceMetadata.
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
-        mMockNetworkProviderWorker = new MockNetworkProviderWorker(mContext,
-                PROVIDER_MODEL_SLICE_URI);
+        mMockNetworkProviderWorker = spy(new MockNetworkProviderWorker(mContext,
+                PROVIDER_MODEL_SLICE_URI));
         mMockProviderModelSlice = new MockProviderModelSlice(mContext, mMockNetworkProviderWorker);
         mListBuilder = spy(new ListBuilder(mContext, PROVIDER_MODEL_SLICE_URI,
                 ListBuilder.INFINITY).setAccentColor(-1));
         when(mProviderModelSliceHelper.createListBuilder(PROVIDER_MODEL_SLICE_URI)).thenReturn(
                 mListBuilder);
+        when(mProviderModelSliceHelper.getSubscriptionManager()).thenReturn(mSubscriptionManager);
 
         mWifiList = new ArrayList<>();
         mMockNetworkProviderWorker.updateSelfResults(mWifiList);
@@ -330,4 +333,33 @@
             return mNetworkProviderWorker;
         }
     }
+
+    @Test
+    public void onNotifyChange_intentToggleActionOn_shouldSetCarrierNetworkEnabledTrue() {
+        Intent intent = mMockProviderModelSlice.getBroadcastIntent(mContext).getIntent();
+        intent.putExtra(EXTRA_TOGGLE_STATE, true);
+
+        mMockProviderModelSlice.onNotifyChange(intent);
+
+        verify(mMockNetworkProviderWorker).setCarrierNetworkEnabled(true);
+    }
+
+    @Test
+    public void onNotifyChange_intentToggleActionOff_shouldSetCarrierNetworkEnabledFalse() {
+        Intent intent = mMockProviderModelSlice.getBroadcastIntent(mContext).getIntent();
+        intent.putExtra(EXTRA_TOGGLE_STATE, false);
+
+        mMockProviderModelSlice.onNotifyChange(intent);
+
+        verify(mMockNetworkProviderWorker).setCarrierNetworkEnabled(false);
+    }
+
+    @Test
+    public void onNotifyChange_intentPrimaryAction_shouldConnectCarrierNetwork() {
+        Intent intent = mMockProviderModelSlice.getBroadcastIntent(mContext).getIntent();
+
+        mMockProviderModelSlice.onNotifyChange(intent);
+
+        verify(mMockNetworkProviderWorker).connectCarrierNetwork();
+    }
 }
diff --git a/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
new file mode 100644
index 0000000..5ee4c42
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/SubscriptionsPreferenceControllerTest.java
@@ -0,0 +1,649 @@
+/*
+ * 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 android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GOOD;
+import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Looper;
+import android.provider.Settings;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+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;
+
+import com.android.settings.Utils;
+import com.android.settings.network.SubscriptionsPreferenceController.SubsPrefCtrlInjector;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SubscriptionsPreferenceControllerTest {
+    private static final String KEY = "preference_group";
+
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
+    @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private TelephonyManager mTelephonyManagerForSub;
+    @Mock
+    private Network mActiveNetwork;
+    @Mock
+    private Lifecycle mLifecycle;
+    @Mock
+    private LifecycleOwner mLifecycleOwner;
+    private LifecycleRegistry mLifecycleRegistry;
+    private int mOnChildUpdatedCount;
+    private Context mContext;
+    private SubscriptionsPreferenceController.UpdateListener mUpdateListener;
+    private PreferenceCategory mPreferenceCategory;
+    private PreferenceScreen mPreferenceScreen;
+    private PreferenceManager mPreferenceManager;
+    private NetworkCapabilities mNetworkCapabilities;
+
+    private FakeSubscriptionsPreferenceController mController;
+    private static SubsPrefCtrlInjector sInjector;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
+
+        when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+        when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+        when(mConnectivityManager.getActiveNetwork()).thenReturn(mActiveNetwork);
+        when(mConnectivityManager.getNetworkCapabilities(mActiveNetwork))
+                .thenReturn(mNetworkCapabilities);
+        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry);
+
+        mPreferenceManager = new PreferenceManager(mContext);
+        mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext);
+        mPreferenceScreen.setInitialExpandedChildrenCount(3);
+        mPreferenceCategory = new PreferenceCategory(mContext);
+        mPreferenceCategory.setKey(KEY);
+        mPreferenceCategory.setOrderingAsAdded(true);
+        mPreferenceScreen.addPreference(mPreferenceCategory);
+
+        mOnChildUpdatedCount = 0;
+        mUpdateListener = () -> mOnChildUpdatedCount++;
+        sInjector = spy(new SubsPrefCtrlInjector());
+        initializeMethod(true, 1, 1, 1, false, false);
+        mController =  new FakeSubscriptionsPreferenceController(mContext, mLifecycle,
+                mUpdateListener, KEY, 5);
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
+    }
+
+    @After
+    public void tearDown() {
+        SubscriptionUtil.setActiveSubscriptionsForTesting(null);
+    }
+
+    @Test
+    public void isAvailable_oneSubscription_availableFalse() {
+        setupMockSubscriptions(1);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_oneSubAndProviderOn_availableTrue() {
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        setupMockSubscriptions(1);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_twoSubscriptions_availableTrue() {
+        setupMockSubscriptions(2);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_fiveSubscriptions_availableTrue() {
+        doReturn(true).when(sInjector).canSubscriptionBeDisplayed(mContext, 0);
+        setupMockSubscriptions(5);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_airplaneModeOn_availableFalse() {
+        setupMockSubscriptions(2);
+
+        assertThat(mController.isAvailable()).isTrue();
+
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    @UiThreadTest
+    public void onAirplaneModeChanged_airplaneModeTurnedOn_eventFired() {
+        setupMockSubscriptions(2);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isTrue();
+
+        final int updateCountBeforeModeChange = mOnChildUpdatedCount;
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        mController.onAirplaneModeChanged(true);
+
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onAirplaneModeChanged_airplaneModeTurnedOff_eventFired() {
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+        setupMockSubscriptions(2);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+        assertThat(mController.isAvailable()).isFalse();
+
+        final int updateCountBeforeModeChange = mOnChildUpdatedCount;
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0);
+
+        mController.onAirplaneModeChanged(true);
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeModeChange + 1);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSubscriptionsChanged_countBecameTwo_eventFired() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
+        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1));
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isFalse();
+
+        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+        SubscriptionUtil.setActiveSubscriptionsForTesting(subs);
+
+        mController.onSubscriptionsChanged();
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSubscriptionsChanged_countBecameOne_eventFiredAndPrefsRemoved() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(2);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
+
+        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 1));
+
+        mController.onSubscriptionsChanged();
+
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onSubscriptionsChanged_subscriptionReplaced_preferencesChanged() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(3);
+
+        // Start out with only sub1 and sub2.
+        SubscriptionUtil.setActiveSubscriptionsForTesting(subs.subList(0, 2));
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub2");
+        assertThat(mPreferenceCategory.getPreference(1).getTitle()).isEqualTo("sub1");
+
+        // Now replace sub2 with sub3, and make sure the old preference was removed and the new
+        // preference was added.
+        final int updateCountBeforeSubscriptionChange = mOnChildUpdatedCount;
+        SubscriptionUtil.setActiveSubscriptionsForTesting(Arrays.asList(subs.get(0), subs.get(2)));
+        mController.onSubscriptionsChanged();
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mOnChildUpdatedCount).isEqualTo(updateCountBeforeSubscriptionChange + 1);
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(2);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub3");
+        assertThat(mPreferenceCategory.getPreference(1).getTitle()).isEqualTo("sub1");
+    }
+
+    @Test
+    public void getSummary_twoSubsOneDefaultForEverythingDataActive() {
+        setupMockSubscriptions(2);
+
+        doReturn(11).when(sInjector).getDefaultSmsSubscriptionId();
+        doReturn(11).when(sInjector).getDefaultVoiceSubscriptionId();
+        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
+        doReturn(true).when(sInjector).isActiveCellularNetwork(mContext);
+
+        assertThat(mController.getSummary(11, true)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "default_for_calls_and_sms")
+                        + System.lineSeparator()
+                        + ResourcesUtils.getResourcesString(mContext, "mobile_data_active"));
+
+        assertThat(mController.getSummary(22, false)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "subscription_available"));
+    }
+
+    @Test
+    public void getSummary_twoSubsOneDefaultForEverythingDataNotActive() {
+        setupMockSubscriptions(2, 1, true);
+
+        doReturn(1).when(sInjector).getDefaultSmsSubscriptionId();
+        doReturn(1).when(sInjector).getDefaultVoiceSubscriptionId();
+
+        assertThat(mController.getSummary(1, true)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "default_for_calls_and_sms")
+                        + System.lineSeparator()
+                        + ResourcesUtils.getResourcesString(mContext, "default_for_mobile_data"));
+
+        assertThat(mController.getSummary(2, false)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "subscription_available"));
+    }
+
+    @Test
+    public void getSummary_twoSubsOneDefaultForEverythingDataDisabled() {
+        setupMockSubscriptions(2);
+
+        doReturn(1).when(sInjector).getDefaultSmsSubscriptionId();
+        doReturn(1).when(sInjector).getDefaultVoiceSubscriptionId();
+
+        assertThat(mController.getSummary(1, true)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "default_for_calls_and_sms")
+                        + System.lineSeparator()
+                        + ResourcesUtils.getResourcesString(mContext, "mobile_data_off"));
+
+        assertThat(mController.getSummary(2, false)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "subscription_available"));
+    }
+
+    @Test
+    public void getSummary_twoSubsOneForCallsAndDataOneForSms() {
+        setupMockSubscriptions(2, 1, true);
+
+        doReturn(2).when(sInjector).getDefaultSmsSubscriptionId();
+        doReturn(1).when(sInjector).getDefaultVoiceSubscriptionId();
+
+        assertThat(mController.getSummary(1, true)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "default_for_calls")
+                        + System.lineSeparator()
+                        + ResourcesUtils.getResourcesString(mContext, "default_for_mobile_data"));
+
+        assertThat(mController.getSummary(2, false)).isEqualTo(
+                ResourcesUtils.getResourcesString(mContext, "default_for_sms"));
+    }
+
+    @Test
+    @UiThreadTest
+    public void setIcon_greatSignal_correctLevels() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(2, 1, true);
+        setMockSubSignalStrength(subs, 0, SIGNAL_STRENGTH_GREAT);
+        setMockSubSignalStrength(subs, 1, SIGNAL_STRENGTH_GREAT);
+        final Preference pref = new Preference(mContext);
+        final Drawable greatDrawWithoutCutOff = mock(Drawable.class);
+        doReturn(greatDrawWithoutCutOff).when(sInjector)
+                .getIcon(mContext, 4, NUM_SIGNAL_STRENGTH_BINS, true);
+
+        mController.setIcon(pref, 1, true /* isDefaultForData */);
+        assertThat(pref.getIcon()).isEqualTo(greatDrawWithoutCutOff);
+
+        final Drawable greatDrawWithCutOff = mock(Drawable.class);
+        doReturn(greatDrawWithCutOff).when(sInjector)
+                .getIcon(mContext, 4, NUM_SIGNAL_STRENGTH_BINS, true);
+        mController.setIcon(pref, 2, false /* isDefaultForData */);
+        assertThat(pref.getIcon()).isEqualTo(greatDrawWithCutOff);
+    }
+
+    @Test
+    @UiThreadTest
+    public void displayPreference_providerAndHasSim_showPreference() {
+        final List<SubscriptionInfo> sub = setupMockSubscriptions(1);
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub1");
+    }
+
+    @Test
+    @UiThreadTest
+    public void displayPreference_providerAndHasMultiSim_showDataSubPreference() {
+        final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub1");
+    }
+
+    @Test
+    @UiThreadTest
+    public void displayPreference_providerAndNoSim_noPreference() {
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(null).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onAirplaneModeChanged_providerAndHasSim_noPreference() {
+        setupMockSubscriptions(1);
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
+
+        mController.onAirplaneModeChanged(true);
+
+        assertThat(mController.isAvailable()).isFalse();
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
+    }
+
+    @Test
+    @UiThreadTest
+    public void dataSubscriptionChanged_providerAndHasMultiSim_showSubId1Preference() {
+        final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+        Intent intent = new Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+        mController.mDataSubscriptionChangedReceiver.onReceive(mContext, intent);
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub1");
+    }
+
+    @Test
+    @UiThreadTest
+    public void dataSubscriptionChanged_providerAndHasMultiSim_showSubId2Preference() {
+        final List<SubscriptionInfo> sub = setupMockSubscriptions(2);
+        final int subId = sub.get(0).getSubscriptionId();
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+        Intent intent = new Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub1");
+
+        doReturn(sub.get(1)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+
+        mController.mDataSubscriptionChangedReceiver.onReceive(mContext, intent);
+
+        assertThat(mController.isAvailable()).isTrue();
+        assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
+        assertThat(mPreferenceCategory.getPreference(0).getTitle()).isEqualTo("sub2");
+    }
+
+    @Test
+    @UiThreadTest
+    public void getIcon_cellularIsActive_iconColorIsAccentDefaultColor() {
+        final List<SubscriptionInfo> sub = setupMockSubscriptions(1);
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(sub.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+        Drawable icon = mock(Drawable.class);
+        doReturn(icon).when(sInjector).getIcon(any(), anyInt(), anyInt(), eq(false));
+        setupGetIconConditions(sub.get(0).getSubscriptionId(), true, true,
+                TelephonyManager.DATA_CONNECTED, ServiceState.STATE_IN_SERVICE);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+
+        verify(icon).setTint(Utils.getColorAccentDefaultColor(mContext));
+    }
+
+    @Test
+    @UiThreadTest
+    public void getIcon_dataStateConnectedAndMobileDataOn_iconIsSignalIcon() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(1);
+        final int subId = subs.get(0).getSubscriptionId();
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(subs.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+        Drawable icon = mock(Drawable.class);
+        doReturn(icon).when(sInjector).getIcon(any(), anyInt(), anyInt(), eq(false));
+        setupGetIconConditions(subId, false, true,
+                TelephonyManager.DATA_CONNECTED, ServiceState.STATE_IN_SERVICE);
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+        Drawable actualIcon = mPreferenceCategory.getPreference(0).getIcon();
+
+        assertThat(icon).isEqualTo(actualIcon);
+    }
+
+    @Test
+    @UiThreadTest
+    public void getIcon_voiceInServiceAndMobileDataOff_iconIsSignalIcon() {
+        final List<SubscriptionInfo> subs = setupMockSubscriptions(1);
+        final int subId = subs.get(0).getSubscriptionId();
+        doReturn(true).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(subs.get(0)).when(mSubscriptionManager).getDefaultDataSubscriptionInfo();
+        Drawable icon = mock(Drawable.class);
+        doReturn(icon).when(sInjector).getIcon(any(), anyInt(), anyInt(), eq(false));
+
+        setupGetIconConditions(subId, false, false,
+                TelephonyManager.DATA_DISCONNECTED, ServiceState.STATE_IN_SERVICE);
+
+        mController.onResume();
+        mController.displayPreference(mPreferenceScreen);
+        Drawable actualIcon = mPreferenceCategory.getPreference(0).getIcon();
+        doReturn(TelephonyManager.DATA_CONNECTED).when(mTelephonyManagerForSub).getDataState();
+
+        assertThat(icon).isEqualTo(actualIcon);
+    }
+
+    private void setupGetIconConditions(int subId, boolean isActiveCellularNetwork,
+            boolean isDataEnable, int dataState, int servicestate) {
+        doReturn(mTelephonyManagerForSub).when(mTelephonyManager).createForSubscriptionId(subId);
+        doReturn(isActiveCellularNetwork).when(sInjector).isActiveCellularNetwork(mContext);
+        doReturn(isDataEnable).when(mTelephonyManagerForSub).isDataEnabled();
+        doReturn(dataState).when(mTelephonyManagerForSub).getDataState();
+        ServiceState ss = mock(ServiceState.class);
+        doReturn(ss).when(mTelephonyManagerForSub).getServiceState();
+        doReturn(servicestate).when(ss).getState();
+    }
+
+    private List<SubscriptionInfo> setupMockSubscriptions(int count) {
+        return setupMockSubscriptions(count, 0, true);
+    }
+
+    /** Helper method to setup several mock active subscriptions. The generated subscription id's
+     * start at 1.
+     *
+     * @param count How many subscriptions to create
+     * @param defaultDataSubId The subscription id of the default data subscription - pass
+     *                         INVALID_SUBSCRIPTION_ID if there should not be one
+     * @param mobileDataEnabled Whether mobile data should be considered enabled for the default
+     *                          data subscription
+     */
+    private List<SubscriptionInfo> setupMockSubscriptions(int count, int defaultDataSubId,
+            boolean mobileDataEnabled) {
+        if (defaultDataSubId != INVALID_SUBSCRIPTION_ID) {
+            when(sInjector.getDefaultDataSubscriptionId()).thenReturn(defaultDataSubId);
+        }
+        final ArrayList<SubscriptionInfo> infos = new ArrayList<>();
+        for (int i = 0; i < count; i++) {
+            final int subscriptionId = i + 1;
+            final SubscriptionInfo info = mock(SubscriptionInfo.class);
+            final TelephonyManager mgrForSub = mock(TelephonyManager.class);
+            final SignalStrength signalStrength = mock(SignalStrength.class);
+
+            if (subscriptionId == defaultDataSubId) {
+                when(mgrForSub.isDataEnabled()).thenReturn(mobileDataEnabled);
+            }
+            when(info.getSubscriptionId()).thenReturn(subscriptionId);
+            when(info.getDisplayName()).thenReturn("sub" + (subscriptionId));
+            doReturn(mgrForSub).when(mTelephonyManager).createForSubscriptionId(eq(subscriptionId));
+            when(mgrForSub.getSignalStrength()).thenReturn(signalStrength);
+            when(signalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GOOD);
+            doReturn(true).when(sInjector).canSubscriptionBeDisplayed(mContext, subscriptionId);
+            infos.add(info);
+        }
+        SubscriptionUtil.setActiveSubscriptionsForTesting(infos);
+        return infos;
+    }
+
+    /**
+     * Helper method to set the signal strength returned for a mock subscription
+     * @param subs The list of subscriptions
+     * @param index The index in of the subscription in |subs| to change
+     * @param level The signal strength level to return for the subscription. Pass -1 to force
+     *              return of a null SignalStrength object for the subscription.
+     */
+    private void setMockSubSignalStrength(List<SubscriptionInfo> subs, int index, int level) {
+        final int subId =  subs.get(index).getSubscriptionId();
+        doReturn(mTelephonyManagerForSub).when(mTelephonyManager).createForSubscriptionId(subId);
+        if (level == -1) {
+            when(mTelephonyManagerForSub.getSignalStrength()).thenReturn(null);
+        } else {
+            final SignalStrength signalStrength = mock(SignalStrength.class);
+            doReturn(signalStrength).when(mTelephonyManagerForSub).getSignalStrength();
+            when(signalStrength.getLevel()).thenReturn(level);
+        }
+    }
+
+    private void initializeMethod(boolean isSubscriptionCanBeDisplayed,
+            int defaultSmsSubscriptionId, int defaultVoiceSubscriptionId,
+            int defaultDataSubscriptionId, boolean isActiveCellularNetwork,
+            boolean isProviderModelEnabled) {
+        doReturn(isSubscriptionCanBeDisplayed)
+                .when(sInjector).canSubscriptionBeDisplayed(mContext, eq(anyInt()));
+        doReturn(defaultSmsSubscriptionId).when(sInjector).getDefaultSmsSubscriptionId();
+        doReturn(defaultVoiceSubscriptionId).when(sInjector).getDefaultVoiceSubscriptionId();
+        doReturn(defaultDataSubscriptionId).when(sInjector).getDefaultDataSubscriptionId();
+        doReturn(isActiveCellularNetwork).when(sInjector).isActiveCellularNetwork(mContext);
+        doReturn(isProviderModelEnabled).when(sInjector).isProviderModelEnabled(mContext);
+        doReturn(mock(Drawable.class))
+                .when(sInjector).getIcon(any(), anyInt(), anyInt(), eq(false));
+    }
+
+    private static class FakeSubscriptionsPreferenceController
+            extends SubscriptionsPreferenceController {
+
+        /**
+         * @param context            the context for the UI where we're placing these preferences
+         * @param lifecycle          for listening to lifecycle events for the UI
+         * @param updateListener     called to let our parent controller know that our
+         *                           availability has
+         *                           changed, or that one or more of the preferences we've placed
+         *                           in the
+         *                           PreferenceGroup has changed
+         * @param preferenceGroupKey the key used to lookup the PreferenceGroup where Preferences
+         *                          will
+         *                           be placed
+         * @param startOrder         the order that should be given to the first Preference
+         *                           placed into
+         *                           the PreferenceGroup; the second will use startOrder+1, third
+         *                           will
+         *                           use startOrder+2, etc. - this is useful for when the parent
+         *                           wants
+         *                           to have other preferences in the same PreferenceGroup and wants
+         */
+        FakeSubscriptionsPreferenceController(Context context, Lifecycle lifecycle,
+                UpdateListener updateListener, String preferenceGroupKey, int startOrder) {
+            super(context, lifecycle, updateListener, preferenceGroupKey, startOrder);
+        }
+
+        @Override
+        protected SubsPrefCtrlInjector createSubsPrefCtrlInjector() {
+            return sInjector;
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java b/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
index 7a75443..1a5e852 100644
--- a/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
+++ b/tests/unit/src/com/android/settings/wifi/WifiUtilsTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -46,12 +47,48 @@
     public void testPassword() {
         final String longPassword = "123456789012345678901234567890"
                 + "1234567890123456789012345678901234567890";
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("123")).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("12345678")).isTrue();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("1234567890")).isTrue();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid(longPassword)).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("")).isFalse();
-        assertThat(WifiUtils.isHotspotWpa2PasswordValid("€¥£")).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)).isFalse();
+
+        // The WPA3_SAE_TRANSITION password limitation should be same as WPA2_PSK
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)).isFalse();
+
+        // The WA3_SAE password is requested that length > 1 only.
+        assertThat(WifiUtils.isHotspotPasswordValid("",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isFalse();
+        assertThat(WifiUtils.isHotspotPasswordValid("1",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("123",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("12345678",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("1234567890",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid(longPassword,
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
+        assertThat(WifiUtils.isHotspotPasswordValid("€¥£",
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)).isTrue();
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
new file mode 100644
index 0000000..836d4e4
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherSecurityPreferenceControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.wifi.tether;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Looper;
+
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+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 WifiTetherSecurityPreferenceControllerTest {
+
+    private static final String PREF_KEY = "wifi_tether_security";
+    private static final String WPA3_SAE =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+    private static final String WPA3_SAE_TRANSITION =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+    private static final String WPA2_PSK =
+            String.valueOf(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+    private static final String NONE = String.valueOf(SoftApConfiguration.SECURITY_TYPE_OPEN);
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+
+    private WifiTetherSecurityPreferenceController mController;
+    private ListPreference mPreference;
+    private SoftApConfiguration mConfig;
+
+    @Before
+    public void setUp() {
+        final Context context = spy(ApplicationProvider.getApplicationContext());
+        mConfig = new SoftApConfiguration.Builder().setSsid("test_1234")
+                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
+        when(context.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(mConfig);
+
+        mController = new WifiTetherSecurityPreferenceController(context, mListener);
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        final PreferenceManager preferenceManager = new PreferenceManager(context);
+        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);
+        mPreference = new ListPreference(context);
+        mPreference.setKey(PREF_KEY);
+        screen.addPreference(mPreference);
+        mController.displayPreference(screen);
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa3Sae_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA3_SAE);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA3-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa3SaeTransition_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA3_SAE_TRANSITION);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2/WPA3-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toWpa2Psk_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, WPA2_PSK);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void onPreferenceChange_toNone_shouldUpdateSecurityValue() {
+        mController.onPreferenceChange(mPreference, NONE);
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3Sae_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA3-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeTransition_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2/WPA3-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa2Psk_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toNone_shouldUpdateSecurityValue() {
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("None");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeButNotSupportWpa3_shouldBeDefaultToWpa2() {
+        mController.mIsWpa3Supported = false;
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+
+    @Test
+    public void updateDisplay_toWpa3SaeTransitionButNotSupportWpa3_shouldBeDefaultToWpa2() {
+        mController.mIsWpa3Supported = false;
+        SoftApConfiguration config = new SoftApConfiguration.Builder(mConfig)
+                .setPassphrase("test_password",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION).build();
+        when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
+
+        mController.updateDisplay();
+
+        assertThat(mController.getSecurityType())
+                .isEqualTo(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+        assertThat(mPreference.getSummary().toString()).isEqualTo("WPA2-Personal");
+    }
+}