Merge "Update BatterySettingsFeatureProvider interface" into udc-qpr-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a78e74a..6ce7bdf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10542,8 +10542,6 @@
     <string name="platform_compat_default_disabled_title">Default disabled changes</string>
     <!-- Title for target SDK gated app compat changes category (do not translate 'targetSdkVersion') [CHAR LIMIT=50] -->
     <string name="platform_compat_target_sdk_title">Enabled for targetSdkVersion &gt;= <xliff:g id="number" example="29">%d</xliff:g></string>
-    <!-- Title for the dialog shown when no debuggable apps are available [CHAR LIMIT=30] -->
-    <string name="platform_compat_dialog_title_no_apps">No apps available</string>
     <!-- Explanatory text shown when no debuggable apps are available [CHAR LIMIT=NONE] -->
     <string name="platform_compat_dialog_text_no_apps">App compatibility changes can only be modified for debuggable apps. Install a debuggable app and try again.</string>
 
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index b26005a..32acac6 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -258,7 +258,7 @@
             android:key="platform_compat_dashboard"
             android:title="@string/platform_compat_dashboard_title"
             android:summary="@string/platform_compat_dashboard_summary"
-            android:fragment="com.android.settings.development.compat.PlatformCompatDashboard"
+            settings:controller="com.android.settings.spa.development.compat.PlatformCompatPreferenceController"
             />
 
         <SwitchPreference
diff --git a/res/layout/wifi_api_test.xml b/res/xml/wifi_api_test.xml
similarity index 100%
rename from res/layout/wifi_api_test.xml
rename to res/xml/wifi_api_test.xml
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
index 92786c9..a0b249d 100644
--- a/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeActivity.java
@@ -98,7 +98,7 @@
                         BluetoothBroadcastUtils.TAG_FRAGMENT_QR_CODE_SCANNER);
 
         if (fragment == null) {
-            fragment = new QrCodeScanModeFragment(mIsGroupOp, mSink);
+            fragment = new QrCodeScanModeFragment();
         } else {
             if (fragment.isVisible()) {
                 return;
diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
index f89dac6..80aedd7 100644
--- a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
+++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.app.settings.SettingsEnums;
-import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Matrix;
@@ -71,9 +70,7 @@
 
     public static final String KEY_BROADCAST_METADATA = "key_broadcast_metadata";
 
-    private boolean mIsGroupOp;
     private int mCornerRadius;
-    private BluetoothDevice mSink;
     private String mBroadcastMetadata;
     private Context mContext;
     private QrCamera mCamera;
@@ -81,11 +78,6 @@
     private TextView mSummary;
     private TextView mErrorMessage;
 
-    public QrCodeScanModeFragment(boolean isGroupOp, BluetoothDevice sink) {
-        mIsGroupOp = isGroupOp;
-        mSink = sink;
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
index 0d91fdd..b7b2759 100644
--- a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
+++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java
@@ -25,12 +25,4 @@
     int REQUEST_CODE_DEBUG_APP = 1;
 
     int REQUEST_MOCK_LOCATION_APP = 2;
-
-    int REQUEST_CODE_ANGLE_ALL_USE_ANGLE = 3;
-
-    int REQUEST_CODE_ANGLE_DRIVER_PKGS = 4;
-
-    int REQUEST_CODE_ANGLE_DRIVER_VALUES = 5;
-
-    int REQUEST_COMPAT_CHANGE_APP = 6;
 }
diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
index f8cbf21..3f0ffc7 100644
--- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java
+++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
@@ -17,21 +17,16 @@
 package com.android.settings.development.compat;
 
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
-import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_COMPAT_CHANGE_APP;
 
-import android.app.Activity;
-import android.app.AlertDialog;
 import android.app.settings.SettingsEnums;
 import android.compat.Compatibility.ChangeConfig;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.ArraySet;
 
 import androidx.annotation.VisibleForTesting;
@@ -40,35 +35,28 @@
 import androidx.preference.PreferenceCategory;
 import androidx.preference.SwitchPreference;
 
-import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.settings.R;
 import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.development.AppPicker;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
-
 /**
  * Dashboard for Platform Compat preferences.
  */
 public class PlatformCompatDashboard extends DashboardFragment {
     private static final String TAG = "PlatformCompatDashboard";
-    private static final String COMPAT_APP = "compat_app";
+    public static final String COMPAT_APP = "compat_app";
 
     private IPlatformCompat mPlatformCompat;
 
     private CompatibilityChangeInfo[] mChanges;
 
-    private AndroidBuildClassifier mAndroidBuildClassifier = new AndroidBuildClassifier();
-
-    private boolean mShouldStartAppPickerOnResume = true;
-
     @VisibleForTesting
     String mSelectedApp;
 
@@ -108,32 +96,6 @@
         } catch (RemoteException e) {
             throw new RuntimeException("Could not list changes!", e);
         }
-        if (icicle != null) {
-            mShouldStartAppPickerOnResume = false;
-            mSelectedApp = icicle.getString(COMPAT_APP);
-        }
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_COMPAT_CHANGE_APP) {
-            mShouldStartAppPickerOnResume = false;
-            switch (resultCode) {
-                case Activity.RESULT_OK:
-                    mSelectedApp = data.getAction();
-                    break;
-                case Activity.RESULT_CANCELED:
-                    if (TextUtils.isEmpty(mSelectedApp)) {
-                        finish();
-                    }
-                    break;
-                case AppPicker.RESULT_NO_MATCHING_APPS:
-                    mSelectedApp = null;
-                    break;
-            }
-            return;
-        }
-        super.onActivityResult(requestCode, resultCode, data);
     }
 
     @Override
@@ -142,33 +104,18 @@
         if (isFinishingOrDestroyed()) {
             return;
         }
-        if (!mShouldStartAppPickerOnResume) {
-            if (TextUtils.isEmpty(mSelectedApp)) {
-                new AlertDialog.Builder(getContext())
-                        .setTitle(R.string.platform_compat_dialog_title_no_apps)
-                        .setMessage(R.string.platform_compat_dialog_text_no_apps)
-                        .setPositiveButton(R.string.okay, (dialog, which) -> finish())
-                        .setOnDismissListener(dialog -> finish())
-                        .setCancelable(false)
-                        .show();
-                return;
-            }
-            try {
-                final ApplicationInfo applicationInfo = getApplicationInfo();
-                addPreferences(applicationInfo);
-                return;
-            } catch (PackageManager.NameNotFoundException e) {
-                mShouldStartAppPickerOnResume = true;
-                mSelectedApp = null;
-            }
+        Bundle arguments = getArguments();
+        if (arguments == null) {
+            finish();
+            return;
         }
-        startAppPicker();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putString(COMPAT_APP, mSelectedApp);
+        mSelectedApp = arguments.getString(COMPAT_APP);
+        try {
+            final ApplicationInfo applicationInfo = getApplicationInfo();
+            addPreferences(applicationInfo);
+        } catch (PackageManager.NameNotFoundException ignored) {
+            finish();
+        }
     }
 
     private void addPreferences(ApplicationInfo applicationInfo) {
@@ -266,12 +213,6 @@
         appPreference.setIcon(icon);
         appPreference.setSummary(getString(R.string.platform_compat_selected_app_summary,
                                          mSelectedApp, applicationInfo.targetSdkVersion));
-        appPreference.setKey(mSelectedApp);
-        appPreference.setOnPreferenceClickListener(
-                preference -> {
-                    startAppPicker();
-                    return true;
-                });
         return appPreference;
     }
 
@@ -294,17 +235,6 @@
         }
     }
 
-    private void startAppPicker() {
-        final Intent intent = new Intent(getContext(), AppPicker.class)
-                .putExtra(AppPicker.EXTRA_INCLUDE_NOTHING, false);
-        // If build is neither userdebug nor eng, only include debuggable apps
-        final boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
-        if (!debuggableBuild) {
-            intent.putExtra(AppPicker.EXTRA_DEBUGGABLE, true /* value */);
-        }
-        startActivityForResult(intent, REQUEST_COMPAT_CHANGE_APP);
-    }
-
     private class CompatChangePreferenceChangeListener implements OnPreferenceChangeListener {
         private final long changeId;
 
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 8aabc37..fdafca6 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -20,9 +20,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseIntArray;
-import android.view.View;
 
-import androidx.annotation.IdRes;
+import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -134,7 +133,8 @@
 
     public abstract CharSequence getSummary(Context context);
 
-    @IdRes
+    /** Gets the drawable resource id for the icon. */
+    @DrawableRes
     public abstract int getIconId();
 
     /**
@@ -162,21 +162,12 @@
         preference.setTitle(getTitle(context));
         preference.setSummary(getSummary(context));
         preference.setIcon(getIconId());
-        @IdRes int iconTintColorId = getIconTintColorId();
-        if (iconTintColorId != View.NO_ID) {
-            preference.getIcon().setTint(context.getColor(iconTintColorId));
-        }
         final CardPreference cardPreference = castToCardPreferenceSafely(preference);
         if (cardPreference != null) {
             cardPreference.resetLayoutState();
         }
     }
 
-    /** Returns the color resid for tinting {@link #getIconId()} or {@link View#NO_ID} if none. */
-    public @IdRes int getIconTintColorId() {
-        return View.NO_ID;
-    }
-
     public boolean shouldShowDialog() {
         return mShowDialog;
     }
diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.java b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.java
index 54a106a..8f59f0e 100644
--- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.java
+++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.java
@@ -23,6 +23,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 
+import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.network.MobileDataContentObserver;
@@ -38,6 +39,7 @@
     private TelephonyManager mTelephonyManager;
     private MobileDataContentObserver mMobileDataContentObserver;
     private PreferenceScreen mScreen;
+    private Preference mPreference;
 
     public MmsMessagePreferenceController(Context context, String key) {
         super(context, key);
@@ -61,6 +63,7 @@
     public void onStart() {
         if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             mMobileDataContentObserver.register(mContext, mSubId);
+            updateState(mPreference);
         }
     }
 
@@ -75,6 +78,7 @@
     public void displayPreference(PreferenceScreen screen) {
         super.displayPreference(screen);
         mScreen = screen;
+        mPreference = screen.findPreference(getPreferenceKey());
     }
 
 
diff --git a/src/com/android/settings/network/tether/TetherSettings.java b/src/com/android/settings/network/tether/TetherSettings.java
index 6f6ba8e..5398e9b 100644
--- a/src/com/android/settings/network/tether/TetherSettings.java
+++ b/src/com/android/settings/network/tether/TetherSettings.java
@@ -148,12 +148,13 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        // Even when the UI is restricted, addPreferencesFromResource cannot be omitted.
+        addPreferencesFromResource(R.xml.tether_prefs);
         setIfOnlyAvailableForAdmins(true);
         if (isUiRestricted()) {
             return;
         }
 
-        addPreferencesFromResource(R.xml.tether_prefs);
         mContext = getContext();
         mDataSaverBackend = new DataSaverBackend(mContext);
         mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 56d3f0e..369c4f6 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -43,6 +43,7 @@
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceScreen;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
@@ -63,8 +64,8 @@
 @SearchIndexable
 public class NotificationAccessSettings extends EmptyTextSettings {
     private static final String TAG = "NotifAccessSettings";
-    private static final String ALLOWED_KEY = "allowed";
-    private static final String NOT_ALLOWED_KEY = "not_allowed";
+    static final String ALLOWED_KEY = "allowed";
+    static final String NOT_ALLOWED_KEY = "not_allowed";
     private static final int MAX_CN_LENGTH = 500;
 
     private static final ManagedServiceSettings.Config CONFIG =
@@ -80,9 +81,9 @@
                     .setEmptyText(R.string.no_notification_listeners)
                     .build();
 
-    private NotificationManager mNm;
+    @VisibleForTesting NotificationManager mNm;
     protected Context mContext;
-    private PackageManager mPm;
+    @VisibleForTesting PackageManager mPm;
     private DevicePolicyManager mDpm;
     private ServiceListing mServiceListing;
     private IconDrawableFactory mIconDrawableFactory;
@@ -102,12 +103,6 @@
                 .setNoun(CONFIG.noun)
                 .setSetting(CONFIG.setting)
                 .setTag(CONFIG.tag)
-                .setValidator(info -> {
-                    if (info.getComponentName().flattenToString().length() > MAX_CN_LENGTH) {
-                        return false;
-                    }
-                    return true;
-                })
                 .build();
         mServiceListing.addCallback(this::updateList);
 
@@ -140,7 +135,8 @@
         mServiceListing.setListening(false);
     }
 
-    private void updateList(List<ServiceInfo> services) {
+    @VisibleForTesting
+    void updateList(List<ServiceInfo> services) {
         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId());
 
@@ -153,6 +149,11 @@
         services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
         for (ServiceInfo service : services) {
             final ComponentName cn = new ComponentName(service.packageName, service.name);
+            boolean isAllowed = mNm.isNotificationListenerAccessGranted(cn);
+            if (!isAllowed && cn.flattenToString().length() > MAX_CN_LENGTH) {
+                continue;
+            }
+
             CharSequence title = null;
             try {
                 title = mPm.getApplicationInfoAsUser(
@@ -200,7 +201,7 @@
                         return true;
                     });
             pref.setKey(cn.flattenToString());
-            if (mNm.isNotificationListenerAccessGranted(cn)) {
+            if (isAllowed) {
                 allowedCategory.addPreference(pref);
             } else {
                 notAllowedCategory.addPreference(pref);
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 7b7d41a..dfb4a45 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -124,6 +124,9 @@
 
     static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm,
             String pkg, int userId) {
+        if (cdm == null) {
+            return "";
+        }
         boolean multiple = false;
         StringBuilder sb = new StringBuilder();
 
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index caf5b15..b506005 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -36,6 +36,7 @@
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
 import com.android.settings.spa.development.UsageStatsPageProvider
+import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
 import com.android.settings.spa.home.HomePageProvider
 import com.android.settings.spa.network.NetworkAndInternetPageProvider
 import com.android.settings.spa.notification.AppListNotificationsPageProvider
@@ -83,6 +84,7 @@
                 LanguageAndInputPageProvider,
                 AppLanguagesPageProvider,
                 UsageStatsPageProvider,
+                PlatformCompatAppListPageProvider,
                 BackgroundInstalledAppsPageProvider,
                 CloneAppInfoSettingsProvider,
                 NetworkAndInternetPageProvider,
@@ -95,5 +97,5 @@
     override val logger =
         if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS))
             SpaLogProvider
-        else object: SpaLogger {}
+        else object : SpaLogger {}
 }
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt
new file mode 100644
index 0000000..5f3b4e7
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppList.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.spa.development.compat
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberContext
+import com.android.settingslib.spaprivileged.template.app.AppListPage
+
+object PlatformCompatAppListPageProvider : SettingsPageProvider {
+    override val name = "PlatformCompatAppList"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        AppListPage(
+            title = stringResource(R.string.platform_compat_dashboard_title),
+            listModel = rememberContext(::PlatformCompatAppListModel),
+            noItemMessage = stringResource(R.string.platform_compat_dialog_text_no_apps),
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
new file mode 100644
index 0000000..c6752b9
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatAppListModel.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 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.spa.development.compat
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.Build
+import androidx.compose.runtime.Composable
+import androidx.core.os.bundleOf
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.development.compat.PlatformCompatDashboard
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.util.filterItem
+import com.android.settingslib.spa.framework.util.mapItem
+import com.android.settingslib.spaprivileged.model.app.AppListModel
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.hasFlag
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.template.app.AppListItem
+import com.android.settingslib.spaprivileged.template.app.AppListItemModel
+import kotlinx.coroutines.flow.Flow
+
+data class PlatformCompatAppRecord(
+    override val app: ApplicationInfo,
+) : AppRecord
+
+class PlatformCompatAppListModel(
+    private val context: Context,
+) : AppListModel<PlatformCompatAppRecord> {
+
+    override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
+        appListFlow.mapItem(::PlatformCompatAppRecord)
+
+    override fun filter(
+        userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<PlatformCompatAppRecord>>,
+    ) = recordListFlow.filterItem { record ->
+        Build.IS_DEBUGGABLE || record.app.hasFlag(ApplicationInfo.FLAG_DEBUGGABLE)
+    }
+
+    @Composable
+    override fun getSummary(option: Int, record: PlatformCompatAppRecord) =
+        stateOf(record.app.packageName)
+
+    @Composable
+    override fun AppListItemModel<PlatformCompatAppRecord>.AppItem() {
+        AppListItem { navigateToAppCompat(app = record.app) }
+    }
+
+    private fun navigateToAppCompat(app: ApplicationInfo) {
+        SubSettingLauncher(context)
+            .setDestination(PlatformCompatDashboard::class.qualifiedName)
+            .setSourceMetricsCategory(SettingsEnums.DEVELOPMENT)
+            .setArguments(bundleOf(PlatformCompatDashboard.COMPAT_APP to app.packageName))
+            .setUserHandle(app.userHandle)
+            .launch()
+    }
+}
diff --git a/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt b/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt
new file mode 100644
index 0000000..c0a421c
--- /dev/null
+++ b/src/com/android/settings/spa/development/compat/PlatformCompatPreferenceController.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.spa.development.compat
+
+import android.content.Context
+import androidx.preference.Preference
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+
+class PlatformCompatPreferenceController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+        if (preference.key == mPreferenceKey) {
+            mContext.startSpaActivity(PlatformCompatAppListPageProvider.name)
+            return true
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 2f9031e..402d4b1 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -79,6 +79,7 @@
 
     /** Whether to enable the app_copying fragment. */
     private static final boolean SHOW_APP_COPYING_PREF = false;
+    private static final int MESSAGE_PADDING = 20;
 
     private UserManager mUserManager;
     private UserCapabilities mUserCaps;
@@ -274,6 +275,7 @@
                 context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
         dialogHelper.setTitle(R.string.user_revoke_admin_confirm_title);
         dialogHelper.setMessage(R.string.user_revoke_admin_confirm_message);
+        dialogHelper.setMessagePadding(MESSAGE_PADDING);
         dialogHelper.setPositiveButton(R.string.remove, view -> {
             updateUserAdminStatus(false);
             dialogHelper.getDialog().dismiss();
@@ -294,6 +296,7 @@
                 context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
         dialogHelper.setTitle(com.android.settingslib.R.string.user_grant_admin_title);
         dialogHelper.setMessage(com.android.settingslib.R.string.user_grant_admin_message);
+        dialogHelper.setMessagePadding(MESSAGE_PADDING);
         dialogHelper.setPositiveButton(com.android.settingslib.R.string.user_grant_admin_button,
                 view -> {
                     updateUserAdminStatus(true);
diff --git a/src/com/android/settings/wifi/WifiAPITest.java b/src/com/android/settings/wifi/WifiAPITest.java
index 15465ed..c8bcf7f 100644
--- a/src/com/android/settings/wifi/WifiAPITest.java
+++ b/src/com/android/settings/wifi/WifiAPITest.java
@@ -69,7 +69,7 @@
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-        addPreferencesFromResource(R.layout.wifi_api_test);
+        addPreferencesFromResource(R.xml.wifi_api_test);
 
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
 
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index cf29703..098787c 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -94,7 +94,6 @@
     private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
     private Preference mUpdateAddress;
 
-    private boolean mValidListener = false;
     private boolean mEditableWfcMode = true;
     private boolean mEditableWfcRoamingMode = true;
     private boolean mUseWfcHomeModeForRoaming = false;
@@ -104,7 +103,7 @@
     private ProvisioningManager mProvisioningManager;
     private TelephonyManager mTelephonyManager;
 
-    private final PhoneTelephonyCallback mTelephonyCallback = new PhoneTelephonyCallback();
+    private PhoneTelephonyCallback mTelephonyCallback;
 
     private class PhoneTelephonyCallback extends TelephonyCallback implements
             TelephonyCallback.CallStateListener {
@@ -420,27 +419,20 @@
     @Override
     public void onResume() {
         super.onResume();
-
         updateBody();
-
-        final Context context = getActivity();
-        if (queryImsState(mSubId).isWifiCallingSupported()) {
+        Context context = getActivity();
+        if (mTelephonyCallback == null && queryImsState(mSubId).isWifiCallingSupported()) {
+            mTelephonyCallback = new PhoneTelephonyCallback();
             getTelephonyManagerForSub(mSubId).registerTelephonyCallback(
                     context.getMainExecutor(), mTelephonyCallback);
-
             mSwitchBar.addOnSwitchChangeListener(this);
-
-            mValidListener = true;
         }
-
         context.registerReceiver(mIntentReceiver, mIntentFilter,
                 Context.RECEIVER_EXPORTED_UNAUDITED);
-
         final Intent intent = getActivity().getIntent();
         if (intent.getBooleanExtra(Phone.EXTRA_KEY_ALERT_SHOW, false)) {
             showAlert(intent);
         }
-
         // Register callback for provisioning changes.
         registerProvisioningChangedCallback();
     }
@@ -448,19 +440,13 @@
     @Override
     public void onPause() {
         super.onPause();
-
-        final Context context = getActivity();
-
-        if (mValidListener) {
-            mValidListener = false;
-
+        Context context = getActivity();
+        if (mTelephonyCallback != null) {
             getTelephonyManagerForSub(mSubId).unregisterTelephonyCallback(mTelephonyCallback);
-
+            mTelephonyCallback = null;
             mSwitchBar.removeOnSwitchChangeListener(this);
         }
-
         context.unregisterReceiver(mIntentReceiver);
-
         // Remove callback for provisioning changes.
         unregisterProvisioningChangedCallback();
     }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
index 3513168..ecac4f9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTipTest.java
@@ -18,11 +18,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
-import android.view.View;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.View;
 
-import androidx.annotation.IdRes;
+import androidx.annotation.DrawableRes;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
@@ -45,7 +45,7 @@
 
     private static final String TITLE = "title";
     private static final String SUMMARY = "summary";
-    @IdRes
+    @DrawableRes
     private static final int ICON_ID = R.drawable.ic_fingerprint;
 
     private Context mContext;
diff --git a/tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java
index bf03e82..7fe1187 100644
--- a/tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/network/tether/TetherSettingsTest.java
@@ -131,10 +131,12 @@
     @Test
     @Config(shadows = ShadowRestrictedSettingsFragment.class)
     public void onCreate_isUiRestricted_doNotSetupViewModel() {
+        doNothing().when(mTetherSettings).addPreferencesFromResource(anyInt());
         when(mTetherSettings.isUiRestricted()).thenReturn(true);
 
         mTetherSettings.onCreate(null);
 
+        verify(mTetherSettings).addPreferencesFromResource(anyInt());
         verify(mTetherSettings, never()).setupViewModel();
     }
 
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java
new file mode 100644
index 0000000..e644c29
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.android.settings.notification.NotificationAccessSettings.ALLOWED_KEY;
+import static com.android.settings.notification.NotificationAccessSettings.NOT_ALLOWED_KEY;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import com.google.common.base.Strings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothUtils.class})
+public class NotificationAccessSettingsTest {
+
+    private Context mContext;
+    private NotificationAccessSettings mAccessSettings;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        ShadowApplication shadowApp = ShadowApplication.getInstance();
+        shadowApp.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mAccessSettings = new NotificationAccessSettings();
+        FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).setup().get();
+        activity.getSupportFragmentManager().beginTransaction().add(mAccessSettings, null).commit();
+
+        when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).then(
+                (Answer<ApplicationInfo>) invocation -> {
+                    ApplicationInfo appInfo = mock(ApplicationInfo.class);
+                    when(appInfo.loadLabel(any())).thenReturn(invocation.getArgument(0));
+                    return appInfo;
+                });
+
+        mAccessSettings.mNm = mNotificationManager;
+        mAccessSettings.mPm = mPackageManager;
+        ShadowBluetoothUtils.sLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    }
+
+    @Test
+    public void updateList_enabledLongName_shown() {
+        ComponentName longCn = new ComponentName("test.pkg1",
+                Strings.repeat("Blah", 200) + "Service");
+        ComponentName shortCn = new ComponentName("test.pkg2", "ReasonableService");
+        ArrayList<ServiceInfo> services = new ArrayList<>();
+        services.add(newServiceInfo(longCn.getPackageName(), longCn.getClassName(), 1));
+        services.add(newServiceInfo(shortCn.getPackageName(), shortCn.getClassName(), 2));
+        when(mNotificationManager.isNotificationListenerAccessGranted(any())).thenReturn(true);
+
+        mAccessSettings.updateList(services);
+
+        PreferenceScreen screen = mAccessSettings.getPreferenceScreen();
+        PreferenceCategory allowed = checkNotNull(screen.findPreference(ALLOWED_KEY));
+        PreferenceCategory notAllowed = checkNotNull(screen.findPreference(NOT_ALLOWED_KEY));
+        assertThat(allowed.getPreferenceCount()).isEqualTo(2);
+        assertThat(allowed.getPreference(0).getKey()).isEqualTo(longCn.flattenToString());
+        assertThat(allowed.getPreference(1).getKey()).isEqualTo(shortCn.flattenToString());
+        assertThat(notAllowed.getPreferenceCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateList_disabledLongName_notShown() {
+        ComponentName longCn = new ComponentName("test.pkg1",
+                Strings.repeat("Blah", 200) + "Service");
+        ComponentName shortCn = new ComponentName("test.pkg2", "ReasonableService");
+        ArrayList<ServiceInfo> services = new ArrayList<>();
+        services.add(newServiceInfo(longCn.getPackageName(), longCn.getClassName(), 1));
+        services.add(newServiceInfo(shortCn.getPackageName(), shortCn.getClassName(), 2));
+        when(mNotificationManager.isNotificationListenerAccessGranted(any())).thenReturn(false);
+
+        mAccessSettings.updateList(services);
+
+        PreferenceScreen screen = mAccessSettings.getPreferenceScreen();
+        PreferenceCategory allowed = checkNotNull(screen.findPreference(ALLOWED_KEY));
+        PreferenceCategory notAllowed = checkNotNull(screen.findPreference(NOT_ALLOWED_KEY));
+        assertThat(allowed.getPreferenceCount()).isEqualTo(0);
+        assertThat(notAllowed.getPreferenceCount()).isEqualTo(1);
+        assertThat(notAllowed.getPreference(0).getKey()).isEqualTo(shortCn.flattenToString());
+    }
+
+    private static ServiceInfo newServiceInfo(String packageName, String serviceName, int uid) {
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = packageName;
+        serviceInfo.name = serviceName;
+        serviceInfo.applicationInfo = new ApplicationInfo();
+        serviceInfo.applicationInfo.uid = uid;
+        return serviceInfo;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java
index e8bd27d..56ea33c 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicePreferenceControllerTest.java
@@ -18,15 +18,15 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.Uri;
 
 import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
 import androidx.slice.Slice;
 
 import org.junit.Before;
@@ -80,7 +80,6 @@
 
     @Test
     public void onStop_unregisterObserver() {
-        when(mLiveData.hasActiveObservers()).thenReturn(true);
         mController.onStart();
 
         mController.onStop();
@@ -88,20 +87,18 @@
     }
 
     @Test
-    public void onStop_noActiveObservers_notUnregisterObserver() {
-        when(mLiveData.hasActiveObservers()).thenReturn(false);
+    public void onStop_unregisterObserverAndHasSecurityException_noCrash() {
+        LiveData<Slice> liveData = new LiveData<Slice>() {
+            @Override
+            public void removeObserver(@NonNull Observer<? super Slice> observer) {
+                super.removeObserver(observer);
+                throw new SecurityException("SecurityException Test");
+            }
+        };
+        mController.mLiveData = liveData;
         mController.onStart();
 
         mController.onStop();
-        verify(mLiveData, never()).removeObserver(mController);
-    }
-
-    @Test
-    public void onStop_notRegisterObserver_notUnregisterObserver() {
-        when(mLiveData.hasActiveObservers()).thenReturn(true);
-
-        mController.onStop();
-        verify(mLiveData, never()).removeObserver(mController);
     }
 
     @Test
diff --git a/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
new file mode 100644
index 0000000..78aca85
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/development/compat/PlatformCompatAppListModelTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 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.spa.development.compat
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PackageInfoFlags
+import androidx.compose.runtime.State
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class PlatformCompatAppListModelTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @get:Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    private lateinit var listModel: PlatformCompatAppListModel
+
+    @Before
+    fun setUp() {
+        whenever(context.packageManager).thenReturn(packageManager)
+        whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt()))
+            .thenReturn(emptyList())
+        listModel = PlatformCompatAppListModel(context)
+    }
+
+    @Test
+    fun transform() = runTest {
+        val recordListFlow = listModel.transform(
+            userIdFlow = flowOf(USER_ID),
+            appListFlow = flowOf(listOf(APP)),
+        )
+
+        val recordList = recordListFlow.first()
+        assertThat(recordList).hasSize(1)
+        val record = recordList[0]
+        assertThat(record.app).isSameInstanceAs(APP)
+    }
+
+    @Test
+    fun getSummary() = runTest {
+        val summaryState = getSummaryState(APP)
+
+        assertThat(summaryState.value).isEqualTo(PACKAGE_NAME)
+    }
+
+    private fun getSummaryState(app: ApplicationInfo): State<String> {
+        lateinit var summary: State<String>
+        composeTestRule.setContent {
+            summary = listModel.getSummary(
+                option = 0,
+                record = PlatformCompatAppRecord(app),
+            )
+        }
+        return summary
+    }
+
+    private companion object {
+        const val USER_ID = 0
+        const val PACKAGE_NAME = "package.name"
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java
index a0219a0..127cdfd 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.java
@@ -21,15 +21,20 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
 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.os.Looper;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -63,7 +68,7 @@
         when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
         when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
 
-        mPreference = new SwitchPreference(mContext);
+        mPreference = spy(new SwitchPreference(mContext));
         mController = new MmsMessagePreferenceController(mContext, "mms_message");
         mController.init(SUB_ID);
         mPreference.setKey(mController.getPreferenceKey());
@@ -118,4 +123,20 @@
         verify(mTelephonyManager).setMobileDataPolicyEnabled(
                 TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, false);
     }
+
+    @Test
+    public void onStart_updatePreferenceUiState() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+        preferenceScreen.addPreference(mPreference);
+        mController.displayPreference(preferenceScreen);
+
+        mController.onStart();
+
+        // First is preference initialization, and second is in onStart();
+        verify(mPreference, times(2)).setChecked(anyBoolean());
+    }
 }