Merge "Remove Theme.AlertDialog declaration in accessibility dialog layout"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 938e33d..2ff62cc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -132,7 +132,8 @@
             android:defaultToDeviceProtectedStorage="true"
             android:directBootAware="true"
             android:appComponentFactory="androidx.core.app.CoreComponentFactory"
-            android:gwpAsanMode="always">
+            android:gwpAsanMode="always"
+            android:enableOnBackInvokedCallback="true">
 
         <uses-library android:name="org.apache.http.legacy" />
 
diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml
index 2dd8cd5..761a724 100644
--- a/res/layout/choose_lock_pattern_common.xml
+++ b/res/layout/choose_lock_pattern_common.xml
@@ -37,6 +37,16 @@
         android:paddingRight="0dp">
 
         <TextView
+            android:id="@+id/sud_layout_description"
+            android:text="@string/lockpassword_choose_your_pattern_description"
+            style="@style/SudDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minLines="4"
+            android:paddingStart="?attr/sudMarginStart"
+            android:paddingEnd="?attr/sudMarginEnd"/>
+
+        <TextView
             android:id="@+id/headerText"
             style="@style/SudDescription.Glif"
             android:layout_width="match_parent"
diff --git a/res/values/bools.xml b/res/values/bools.xml
new file mode 100644
index 0000000..25c3e0d
--- /dev/null
+++ b/res/values/bools.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2022, 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.
+*/
+-->
+<resources>
+  <!-- Referenced outside of Settings to confirm bug fixed - bug involved multiple
+  pending intents with difference only in intent extras, which doesn't work. -->
+  <bool name="config_isSafetyCenterLockScreenPendingIntentFixed">true</bool>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 355b4ba..9c0250a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4386,6 +4386,8 @@
     <string name="lockpassword_choose_your_profile_pin_header">Set a work PIN</string>
     <!-- Header on first screen of choose device pattern flow [CHAR LIMIT=40] -->
     <string name="lockpassword_choose_your_pattern_header">Set a pattern</string>
+    <!-- Description on first screen of choose device pattern flow [CHAR LIMIT=NONE] -->
+    <string name="lockpassword_choose_your_pattern_description">For added security, set a pattern to unlock the device</string>
     <!-- Header on first screen of choose work profile pattern flow [CHAR LIMIT=40] -->
     <string name="lockpassword_choose_your_profile_pattern_header">Set a work pattern</string>
 
@@ -9421,12 +9423,12 @@
 
     <!-- app summary of notification app list screen [CHAR LIMIT=100] -->
     <plurals name="notifications_sent_daily">
-        <item quantity="one">~<xliff:g id="number">%d</xliff:g> notification per day</item>
-        <item quantity="other">~<xliff:g id="number">%,d</xliff:g> notifications per day</item>
+        <item quantity="one">About <xliff:g id="number">%d</xliff:g> notification per day</item>
+        <item quantity="other">About <xliff:g id="number">%,d</xliff:g> notifications per day</item>
     </plurals>
     <plurals name="notifications_sent_weekly">
-        <item quantity="one">~<xliff:g id="number">%d</xliff:g> notification per week</item>
-        <item quantity="other">~<xliff:g id="number">%,d</xliff:g> notifications per week</item>
+        <item quantity="one">About <xliff:g id="number">%d</xliff:g> notification per week</item>
+        <item quantity="other">About <xliff:g id="number">%,d</xliff:g> notifications per week</item>
     </plurals>
     <!-- app summary of notification app list screen [CHAR LIMIT=100] -->
     <string name="notifications_sent_never">Never</string>
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 95469d4..bceed73 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -24,7 +24,6 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -43,7 +42,6 @@
 import android.widget.TextView;
 
 import androidx.annotation.AnimRes;
-import androidx.annotation.ColorInt;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -51,7 +49,6 @@
 import androidx.annotation.RawRes;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
-import androidx.core.content.ContextCompat;
 import androidx.core.util.Preconditions;
 import androidx.core.widget.TextViewCompat;
 import androidx.viewpager.widget.PagerAdapter;
@@ -112,17 +109,6 @@
         alertDialog.show();
     }
 
-    static AlertDialog showAccessibilityButtonTutorialDialog(Context context) {
-        final AlertDialog alertDialog = createDialog(context,
-                DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON);
-
-        if (!AccessibilityUtil.isGestureNavigateEnabled(context)) {
-            updateMessageWithIcon(context, alertDialog);
-        }
-
-        return alertDialog;
-    }
-
     static AlertDialog showAccessibilityGestureTutorialDialog(Context context) {
         return createDialog(context, DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE);
     }
@@ -218,50 +204,6 @@
         return alertDialog;
     }
 
-    private static void updateMessageWithIcon(Context context, AlertDialog alertDialog) {
-        final TextView gestureTutorialMessage = alertDialog.findViewById(
-                R.id.button_tutorial_message);
-
-        // Get the textView line height to update [icon] size. Must be called after show()
-        final int lineHeight = gestureTutorialMessage.getLineHeight();
-        gestureTutorialMessage.setText(getMessageStringWithIcon(context, lineHeight));
-    }
-
-    private static SpannableString getMessageStringWithIcon(Context context, int lineHeight) {
-        final String messageString = context
-                .getString(R.string.accessibility_tutorial_dialog_message_button);
-        final SpannableString spannableMessage = SpannableString.valueOf(messageString);
-
-        // Icon
-        final int indexIconStart = messageString.indexOf("%s");
-        final int indexIconEnd = indexIconStart + 2;
-        final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
-        final ImageSpan imageSpan = new ImageSpan(icon);
-        imageSpan.setContentDescription("");
-        icon.setBounds(0, 0, lineHeight, lineHeight);
-        spannableMessage.setSpan(
-                imageSpan, indexIconStart, indexIconEnd,
-                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
-        return spannableMessage;
-    }
-
-    /** Returns the color associated with the specified attribute in the context's theme. */
-    @ColorInt
-    private static int getThemeAttrColor(final Context context, final int attributeColor) {
-        final int colorResId = getAttrResourceId(context, attributeColor);
-        return ContextCompat.getColor(context, colorResId);
-    }
-
-    /** Returns the identifier of the resolved resource assigned to the given attribute. */
-    private static int getAttrResourceId(final Context context, final int attributeColor) {
-        final int[] attrs = {attributeColor};
-        final TypedArray typedArray = context.obtainStyledAttributes(attrs);
-        final int colorResId = typedArray.getResourceId(0, 0);
-        typedArray.recycle();
-        return colorResId;
-    }
-
     private static class TutorialPagerAdapter extends PagerAdapter {
         private final List<TutorialPage> mTutorialPages;
         private TutorialPagerAdapter(List<TutorialPage> tutorialPages) {
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index a2b6182..5456f0a 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -26,7 +26,6 @@
 import android.os.UserManager;
 
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.autofill.PasswordsPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
 import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
@@ -96,7 +95,7 @@
     }
 
     private static void buildAccountPreferenceControllers(
-            Context context, SettingsPreferenceFragment parent, String[] authorities,
+            Context context, DashboardFragment parent, String[] authorities,
             List<AbstractPreferenceController> controllers) {
         final AccountPreferenceController accountPrefController =
                 new AccountPreferenceController(context, parent, authorities,
diff --git a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
index 9ad1206..4661c64 100644
--- a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.autofill.PasswordsPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
@@ -78,7 +77,7 @@
     }
 
     private static void buildAccountPreferenceControllers(
-            Context context, SettingsPreferenceFragment parent, String[] authorities,
+            Context context, DashboardFragment parent, String[] authorities,
             List<AbstractPreferenceController> controllers) {
         final AccountPreferenceController accountPrefController =
                 new AccountPreferenceController(context, parent, authorities,
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index 1458988..8c717f0 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -58,10 +58,10 @@
 
 import com.android.settings.AccessiblePreferenceCategory;
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.RestrictedPreference;
@@ -101,7 +101,7 @@
     private Preference mProfileNotAvailablePreference;
     private String[] mAuthorities;
     private int mAuthoritiesCount = 0;
-    private SettingsPreferenceFragment mFragment;
+    private DashboardFragment mFragment;
     private int mAccountProfileOrder = ORDER_ACCOUNT_PROFILES;
     private AccountRestrictionHelper mHelper;
     private MetricsFeatureProvider mMetricsFeatureProvider;
@@ -145,13 +145,13 @@
         public ArrayMap<String, AccountTypePreference> accountPreferences = new ArrayMap<>();
     }
 
-    public AccountPreferenceController(Context context, SettingsPreferenceFragment parent,
+    public AccountPreferenceController(Context context, DashboardFragment parent,
             String[] authorities, @ProfileSelectFragment.ProfileType int type) {
         this(context, parent, authorities, new AccountRestrictionHelper(context), type);
     }
 
     @VisibleForTesting
-    AccountPreferenceController(Context context, SettingsPreferenceFragment parent,
+    AccountPreferenceController(Context context, DashboardFragment parent,
             String[] authorities, AccountRestrictionHelper helper,
             @ProfileSelectFragment.ProfileType int type) {
         super(context);
@@ -314,6 +314,9 @@
         for (int i = 0; i < profilesCount; i++) {
             updateAccountTypes(mProfiles.valueAt(i));
         }
+
+        // Refresh for the auto-sync preferences
+        mFragment.forceUpdatePreferences();
     }
 
     private void updateProfileUi(final UserInfo userInfo) {
@@ -409,7 +412,6 @@
         return preference;
     }
 
-
     private Preference newManagedProfileSettings() {
         Preference preference = new Preference(mFragment.getPreferenceManager().getContext());
         preference.setKey(PREF_KEY_WORK_PROFILE_SETTING);
diff --git a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
index 1fdd3f6..f64e041 100644
--- a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
@@ -24,7 +24,6 @@
 import android.content.Context;
 
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.autofill.PasswordsPreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
@@ -78,7 +77,7 @@
     }
 
     private static void buildAccountPreferenceControllers(
-            Context context, SettingsPreferenceFragment parent, String[] authorities,
+            Context context, DashboardFragment parent, String[] authorities,
             List<AbstractPreferenceController> controllers) {
         final AccountPreferenceController accountPrefController =
                 new AccountPreferenceController(context, parent, authorities,
diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java
index dc94376..3710528 100644
--- a/src/com/android/settings/biometrics/face/FaceSettings.java
+++ b/src/com/android/settings/biometrics/face/FaceSettings.java
@@ -203,6 +203,10 @@
         if (savedInstanceState != null) {
             mToken = savedInstanceState.getByteArray(KEY_TOKEN);
         }
+
+        final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId);
+        mEnrollButton.setVisible(!hasEnrolled);
+        mRemoveButton.setVisible(hasEnrolled);
     }
 
     @Override
@@ -230,10 +234,6 @@
             mEnrollController.setToken(mToken);
         }
 
-        final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId);
-        mEnrollButton.setVisible(!hasEnrolled);
-        mRemoveButton.setVisible(hasEnrolled);
-
         if (!mFaceFeatureProvider.isAttentionSupported(getContext())) {
             removePreference(FaceSettingsAttentionPreferenceController.KEY);
         }
@@ -261,6 +261,10 @@
                     mEnrollController.setToken(mToken);
                     mConfirmingPassword = false;
                 });
+
+                final boolean hasEnrolled = mFaceManager.hasEnrolledTemplates(mUserId);
+                mEnrollButton.setVisible(!hasEnrolled);
+                mRemoveButton.setVisible(hasEnrolled);
             }
         } else if (requestCode == ENROLL_REQUEST) {
             if (resultCode == RESULT_TIMEOUT) {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index fb0a09d..9d1ed7c 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -415,6 +415,30 @@
         updatePreferenceVisibility(mPreferenceControllers);
     }
 
+    /**
+     * Force update all the preferences in this fragment.
+     */
+    public void forceUpdatePreferences() {
+        final PreferenceScreen screen = getPreferenceScreen();
+        if (screen == null || mPreferenceControllers == null) {
+            return;
+        }
+        for (List<AbstractPreferenceController> controllerList : mPreferenceControllers.values()) {
+            for (AbstractPreferenceController controller : controllerList) {
+                final String key = controller.getPreferenceKey();
+                final Preference preference = findPreference(key);
+                if (preference == null) {
+                    continue;
+                }
+                final boolean available = controller.isAvailable();
+                if (available) {
+                    controller.updateState(preference);
+                }
+                preference.setVisible(available);
+            }
+        }
+    }
+
     @VisibleForTesting
     void updatePreferenceVisibility(
             Map<Class, List<AbstractPreferenceController>> preferenceControllers) {
diff --git a/src/com/android/settings/datetime/DatePreferenceController.java b/src/com/android/settings/datetime/DatePreferenceController.java
index 66026d7..50feca1 100644
--- a/src/com/android/settings/datetime/DatePreferenceController.java
+++ b/src/com/android/settings/datetime/DatePreferenceController.java
@@ -18,15 +18,16 @@
 
 import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
 
-import android.app.Activity;
 import android.app.DatePickerDialog;
 import android.app.time.TimeCapabilities;
 import android.app.time.TimeManager;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeDetectorHelper;
 import android.content.Context;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
+import android.util.Log;
 import android.widget.DatePicker;
 
 import androidx.annotation.VisibleForTesting;
@@ -47,6 +48,7 @@
 
     public static final int DIALOG_DATEPICKER = 0;
 
+    private static final String TAG = "DatePreferenceController";
     private static final String KEY_DATE = "date";
 
     private final DatePreferenceHost mHost;
@@ -96,22 +98,32 @@
         mHost.updateTimeAndDateDisplay(mContext);
     }
 
-    public DatePickerDialog buildDatePicker(Activity activity) {
+    /**
+     * Builds a {@link DatePickerDialog} that can be used to request the current date from the user.
+     */
+    public DatePickerDialog buildDatePicker(
+            Context parentContext, TimeDetectorHelper timeDetectorHelper) {
         final Calendar calendar = Calendar.getInstance();
-        final DatePickerDialog d = new DatePickerDialog(
-                activity,
+        final DatePickerDialog dialog = new DatePickerDialog(
+                parentContext,
                 this,
                 calendar.get(Calendar.YEAR),
                 calendar.get(Calendar.MONTH),
                 calendar.get(Calendar.DAY_OF_MONTH));
-        // The system clock can't represent dates outside this range.
+
+        // Limit the dates the user can pick to a sensible range.
+        DatePicker datePicker = dialog.getDatePicker();
+
         calendar.clear();
-        calendar.set(2007, Calendar.JANUARY, 1);
-        d.getDatePicker().setMinDate(calendar.getTimeInMillis());
+        int minYear = timeDetectorHelper.getManualDateSelectionYearMin();
+        calendar.set(minYear, Calendar.JANUARY, 1);
+        datePicker.setMinDate(calendar.getTimeInMillis());
+
+        int maxYear = timeDetectorHelper.getManualDateSelectionYearMax();
         calendar.clear();
-        calendar.set(2037, Calendar.DECEMBER, 31);
-        d.getDatePicker().setMaxDate(calendar.getTimeInMillis());
-        return d;
+        calendar.set(maxYear, Calendar.DECEMBER, 31);
+        datePicker.setMaxDate(calendar.getTimeInMillis());
+        return dialog;
     }
 
     @VisibleForTesting
@@ -121,13 +133,16 @@
         c.set(Calendar.YEAR, year);
         c.set(Calendar.MONTH, month);
         c.set(Calendar.DAY_OF_MONTH, day);
-        long when = Math.max(c.getTimeInMillis(), DatePreferenceHost.MIN_DATE);
+        long when = c.getTimeInMillis();
 
-        if (when / 1000 < Integer.MAX_VALUE) {
-            TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
-            ManualTimeSuggestion manualTimeSuggestion =
-                    TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
-            timeDetector.suggestManualTime(manualTimeSuggestion);
+        TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+        ManualTimeSuggestion manualTimeSuggestion =
+                TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
+        boolean success = timeDetector.suggestManualTime(manualTimeSuggestion);
+        if (!success) {
+            // This implies the system server is applying tighter bounds than the settings app or
+            // the date/time cannot be set for other reasons, e.g. perhaps "auto time" is turned on.
+            Log.w(TAG, "Unable to set date with suggestion=" + manualTimeSuggestion);
         }
     }
 
diff --git a/src/com/android/settings/datetime/DateTimeSettings.java b/src/com/android/settings/datetime/DateTimeSettings.java
index 3da4234..367146b 100644
--- a/src/com/android/settings/datetime/DateTimeSettings.java
+++ b/src/com/android/settings/datetime/DateTimeSettings.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.settings.SettingsEnums;
+import android.app.timedetector.TimeDetectorHelper;
 import android.content.Context;
 import android.content.Intent;
 
@@ -104,7 +105,7 @@
         switch (id) {
             case DatePreferenceController.DIALOG_DATEPICKER:
                 return use(DatePreferenceController.class)
-                        .buildDatePicker(getActivity());
+                        .buildDatePicker(getActivity(), TimeDetectorHelper.INSTANCE);
             case TimePreferenceController.DIALOG_TIMEPICKER:
                 return use(TimePreferenceController.class)
                         .buildTimePicker(getActivity());
diff --git a/src/com/android/settings/datetime/TimePreferenceController.java b/src/com/android/settings/datetime/TimePreferenceController.java
index 368f1f2..09950fc 100644
--- a/src/com/android/settings/datetime/TimePreferenceController.java
+++ b/src/com/android/settings/datetime/TimePreferenceController.java
@@ -16,13 +16,13 @@
 
 package com.android.settings.datetime;
 
-import android.app.Activity;
 import android.app.TimePickerDialog;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TimeDetector;
 import android.content.Context;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
+import android.util.Log;
 import android.widget.TimePicker;
 
 import androidx.preference.Preference;
@@ -42,6 +42,7 @@
 
     public static final int DIALOG_TIMEPICKER = 1;
 
+    private static final String TAG = "TimePreferenceController";
     private static final String KEY_TIME = "time";
 
     private final DatePreferenceController mDatePreferenceController;
@@ -99,14 +100,17 @@
         // SystemClock time.
     }
 
-    public TimePickerDialog buildTimePicker(Activity activity) {
+    /**
+     * Builds a {@link TimePickerDialog} that can be used to request the current time from the user.
+     */
+    public TimePickerDialog buildTimePicker(Context parentContext) {
         final Calendar calendar = Calendar.getInstance();
         return new TimePickerDialog(
-                activity,
+                parentContext,
                 this,
                 calendar.get(Calendar.HOUR_OF_DAY),
                 calendar.get(Calendar.MINUTE),
-                DateFormat.is24HourFormat(activity));
+                DateFormat.is24HourFormat(parentContext));
     }
 
     void setTime(int hourOfDay, int minute) {
@@ -116,13 +120,16 @@
         c.set(Calendar.MINUTE, minute);
         c.set(Calendar.SECOND, 0);
         c.set(Calendar.MILLISECOND, 0);
-        long when = Math.max(c.getTimeInMillis(), TimePreferenceHost.MIN_DATE);
+        long when = c.getTimeInMillis();
 
-        if (when / 1000 < Integer.MAX_VALUE) {
-            TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
-            ManualTimeSuggestion manualTimeSuggestion =
-                    TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
-            timeDetector.suggestManualTime(manualTimeSuggestion);
+        TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+        ManualTimeSuggestion manualTimeSuggestion =
+                TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
+        boolean success = timeDetector.suggestManualTime(manualTimeSuggestion);
+        if (!success) {
+            // This implies the system server is applying tighter bounds than the settings app or
+            // the date/time cannot be set for other reasons, e.g. perhaps "auto time" is turned on.
+            Log.w(TAG, "Unable to set time with suggestion=" + manualTimeSuggestion);
         }
     }
 }
diff --git a/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java b/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
index 333b9aa..aa1d64e 100644
--- a/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
+++ b/src/com/android/settings/datetime/UpdateTimeAndDateCallback.java
@@ -19,8 +19,5 @@
 import android.content.Context;
 
 public interface UpdateTimeAndDateCallback {
-    // Minimum time is Nov 5, 2007, 0:00.
-    long MIN_DATE = 1194220800000L;
-
     void updateTimeAndDateDisplay(Context context);
 }
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index 611ee24..54935ec 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -85,13 +85,15 @@
 
         for (UserInfo info : infos) {
             final StorageResult result = getAppsAndGamesSize(info.id);
-
+            final Bundle media = new Bundle();
+            media.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, MediaColumns.VOLUME_NAME
+                    + "= '" + MediaStore.VOLUME_EXTERNAL_PRIMARY + "'");
             result.imagesSize = getFilesSize(info.id, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                    null /* queryArgs */);
+                    media /* queryArgs */);
             result.videosSize = getFilesSize(info.id, MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
-                    null /* queryArgs */);
+                    media /* queryArgs */);
             result.audioSize = getFilesSize(info.id, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
-                    null /* queryArgs */);
+                    media /* queryArgs */);
 
             final Bundle documentsAndOtherQueryArgs = new Bundle();
             documentsAndOtherQueryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
@@ -100,13 +102,14 @@
                     + " AND " + FileColumns.MEDIA_TYPE + "!=" + FileColumns.MEDIA_TYPE_AUDIO
                     + " AND " + FileColumns.MIME_TYPE + " IS NOT NULL");
             result.documentsAndOtherSize = getFilesSize(info.id,
-                    MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
+                    MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
                     documentsAndOtherQueryArgs);
 
             final Bundle trashQueryArgs = new Bundle();
             trashQueryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_ONLY);
             result.trashSize = getFilesSize(info.id,
-                    MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), trashQueryArgs);
+                    MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
+                    trashQueryArgs);
 
             results.put(info.id, result);
         }
diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java
index 708ba5e..88241b6 100644
--- a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java
@@ -53,16 +53,16 @@
             return;
         }
 
-        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
-            Log.d(TAG, "is system or default app, disable pref");
-            ((SelectorWithWidgetPreference) preference).setChecked(false);
-            preference.setEnabled(false);
-        } else if (mBatteryOptimizeUtils.getAppOptimizationMode()
+        if (mBatteryOptimizeUtils.getAppOptimizationMode()
                 == BatteryOptimizeUtils.MODE_OPTIMIZED) {
             Log.d(TAG, "is optimized states");
             ((SelectorWithWidgetPreference) preference).setChecked(true);
         } else {
             ((SelectorWithWidgetPreference) preference).setChecked(false);
+            if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+                Log.d(TAG, "is system or default app, disable pref");
+                preference.setEnabled(false);
+            }
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java
index 99e742b..fe896a6 100644
--- a/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/RestrictedPreferenceController.java
@@ -51,16 +51,16 @@
             preference.setEnabled(true);
         }
 
-        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
-            Log.d(TAG, "is system or default app, disable pref");
-            ((SelectorWithWidgetPreference) preference).setChecked(false);
-            preference.setEnabled(false);
-        } else if (mBatteryOptimizeUtils.getAppOptimizationMode()
+        if (mBatteryOptimizeUtils.getAppOptimizationMode()
                 == BatteryOptimizeUtils.MODE_RESTRICTED) {
             Log.d(TAG, "is restricted states");
             ((SelectorWithWidgetPreference) preference).setChecked(true);
         } else {
             ((SelectorWithWidgetPreference) preference).setChecked(false);
+            if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+                Log.d(TAG, "is system or default app, disable pref");
+                preference.setEnabled(false);
+            }
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java
index 244a33c..be4091c 100644
--- a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java
@@ -50,15 +50,16 @@
             preference.setEnabled(true);
         }
 
-        if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
-            Log.d(TAG, "is system or default app, unrestricted states only");
-            ((SelectorWithWidgetPreference) preference).setChecked(true);
-        } else if (mBatteryOptimizeUtils.getAppOptimizationMode()
+        if (mBatteryOptimizeUtils.getAppOptimizationMode()
                 == BatteryOptimizeUtils.MODE_UNRESTRICTED) {
             Log.d(TAG, "is unrestricted states");
             ((SelectorWithWidgetPreference) preference).setChecked(true);
         } else {
             ((SelectorWithWidgetPreference) preference).setChecked(false);
+            if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) {
+                Log.d(TAG, "is system or default app, disable pref");
+                preference.setEnabled(false);
+            }
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
index dc80b84..d549101 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
@@ -347,6 +347,7 @@
                 trapezoidIndex, mBatteryIndexedMap.size(), isForce));
 
         mTrapezoidIndex = trapezoidIndex;
+        mBatteryChartView.setSelectedIndex(mTrapezoidIndex);
         mHandler.post(() -> {
             final long start = System.currentTimeMillis();
             removeAndCacheAllPrefs();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
index 9b6abb0..15a3ca6 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
@@ -56,7 +56,6 @@
     private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
             Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
 
-    private static final int DEFAULT_TRAPEZOID_COUNT = 12;
     private static final int DEFAULT_AXIS_LABEL_COUNT = 4;
     private static final int AXIS_LABEL_GAPS_COUNT = DEFAULT_AXIS_LABEL_COUNT - 1;
     private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
@@ -74,7 +73,6 @@
 
     private int mDividerWidth;
     private int mDividerHeight;
-    private int mTrapezoidCount;
     private float mTrapezoidVOffset;
     private float mTrapezoidHOffset;
     private boolean mIsSlotsClickabled;
@@ -112,9 +110,11 @@
 
     @VisibleForTesting
     Paint mTrapezoidCurvePaint = null;
-    private TrapezoidSlot[] mTrapezoidSlots;
+    @VisibleForTesting
+    TrapezoidSlot[] mTrapezoidSlots;
     // Records the location to calculate selected index.
-    private float mTouchUpEventX = Float.MIN_VALUE;
+    @VisibleForTesting
+    float mTouchUpEventX = Float.MIN_VALUE;
     private BatteryChartViewV2.OnSelectListener mOnSelectListener;
 
     public BatteryChartViewV2(Context context) {
@@ -127,36 +127,28 @@
         // Registers the click event listener.
         setOnClickListener(this);
         setSelectedIndex(SELECTED_INDEX_ALL);
-        setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT);
         setClickable(false);
     }
 
-    /** Sets the total trapezoid count for drawing. */
-    public void setTrapezoidCount(int trapezoidCount) {
-        Log.i(TAG, "trapezoidCount:" + trapezoidCount);
-        mTrapezoidCount = trapezoidCount;
-        mTrapezoidSlots = new TrapezoidSlot[trapezoidCount];
-        // Allocates the trapezoid slot array.
-        for (int index = 0; index < trapezoidCount; index++) {
-            mTrapezoidSlots[index] = new TrapezoidSlot();
-        }
-        invalidate();
-    }
-
     /** Sets all levels value to draw the trapezoid shape */
     public void setLevels(int[] levels) {
         Log.d(TAG, "setLevels() " + (levels == null ? "null" : levels.length));
-        if (levels == null) {
+        // At least 2 levels to draw a trapezoid.
+        if (levels == null || levels.length < 2) {
             mLevels = null;
+            invalidate();
             return;
         }
-        // We should provide trapezoid count + 1 data to draw all trapezoids.
-        mLevels = levels.length == mTrapezoidCount + 1 ? levels : null;
+        mLevels = levels;
+
+        // Initialize trapezoid slots.
+        mTrapezoidSlots = new TrapezoidSlot[mLevels.length - 1];
+        for (int index = 0; index < mTrapezoidSlots.length; index++) {
+            mTrapezoidSlots[index] = new TrapezoidSlot();
+        }
+
         setClickable(false);
         invalidate();
-        if (mLevels == null) {
-            return;
-        }
         // Sets the chart is clickable if there is at least one valid item in it.
         for (int index = 0; index < mLevels.length - 1; index++) {
             if (mLevels[index] != 0 && mLevels[index + 1] != 0) {
@@ -171,10 +163,6 @@
         if (mSelectedIndex != index) {
             mSelectedIndex = index;
             invalidate();
-            // Callbacks to the listener if we have.
-            if (mOnSelectListener != null) {
-                mOnSelectListener.onSelect(mSelectedIndex);
-            }
         }
     }
 
@@ -249,7 +237,12 @@
     @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
+        // Before mLevels initialized, the count of trapezoids is unknown. Only draws the
+        // horizontal percentages and dividers.
         drawHorizontalDividers(canvas);
+        if (mLevels == null) {
+            return;
+        }
         drawVerticalDividers(canvas);
         drawTrapezoids(canvas);
     }
@@ -306,11 +299,9 @@
                 || !isValidToDraw(trapezoidIndex)) {
             return;
         }
-        // Selects all if users click the same trapezoid item two times.
-        if (trapezoidIndex == mSelectedIndex) {
-            setSelectedIndex(SELECTED_INDEX_ALL);
-        } else {
-            setSelectedIndex(trapezoidIndex);
+        if (mOnSelectListener != null) {
+            mOnSelectListener.onSelect(
+                    trapezoidIndex == mSelectedIndex ? SELECTED_INDEX_ALL : trapezoidIndex);
         }
         view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
     }
@@ -440,9 +431,9 @@
 
     private void drawVerticalDividers(Canvas canvas) {
         final int width = getWidth() - mIndent.right;
-        final int dividerCount = mTrapezoidCount + 1;
+        final int dividerCount = mTrapezoidSlots.length + 1;
         final float dividerSpace = dividerCount * mDividerWidth;
-        final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount;
+        final float unitWidth = (width - dividerSpace) / (float) mTrapezoidSlots.length;
         final float bottomY = getHeight() - mIndent.bottom;
         final float startY = bottomY - mDividerHeight;
         final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
@@ -518,7 +509,7 @@
         // Draws all trapezoid shapes into the canvas.
         final Path trapezoidPath = new Path();
         Path trapezoidCurvePath = null;
-        for (int index = 0; index < mTrapezoidCount; index++) {
+        for (int index = 0; index < mTrapezoidSlots.length; index++) {
             // Not draws the trapezoid for corner or not initialization cases.
             if (!isValidToDraw(index)) {
                 if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
@@ -619,7 +610,8 @@
     }
 
     // A container class for each trapezoid left and right location.
-    private static final class TrapezoidSlot {
+    @VisibleForTesting
+    static final class TrapezoidSlot {
         public float mLeft;
         public float mRight;
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
new file mode 100644
index 0000000..5743cac
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Wraps the battery usage diff data for each entry used for battery usage app list. */
+public class BatteryDiffData {
+    private final List<BatteryDiffEntry> mAppEntries;
+    private final List<BatteryDiffEntry> mSystemEntries;
+
+    public BatteryDiffData(
+            List<BatteryDiffEntry> appDiffEntries, List<BatteryDiffEntry> systemDiffEntries) {
+        mAppEntries = appDiffEntries == null ? new ArrayList<>() : appDiffEntries;
+        mSystemEntries = systemDiffEntries == null ? new ArrayList<>() : systemDiffEntries;
+    }
+
+    public List<BatteryDiffEntry> getAppDiffEntryList() {
+        return mAppEntries;
+    }
+
+    public List<BatteryDiffEntry> getSystemDiffEntryList() {
+        return mSystemEntries;
+    }
+
+    /** Sets total consume power for each entry. */
+    public void setTotalConsumePowerForAllEntries(double totalConsumePower) {
+        mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
+        mSystemEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
+    }
+}
diff --git a/src/com/android/settings/notification/AppBubbleListPreferenceController.java b/src/com/android/settings/notification/AppBubbleListPreferenceController.java
index bf7fcc0..6ebb376 100644
--- a/src/com/android/settings/notification/AppBubbleListPreferenceController.java
+++ b/src/com/android/settings/notification/AppBubbleListPreferenceController.java
@@ -51,7 +51,7 @@
     private static final String KEY = "bubble_conversations";
 
     public AppBubbleListPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -80,25 +80,25 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         // copy rather than inherit super's isAvailable because apps can link to this page
         // as part of onboarding, before they send a valid conversation notification
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
                     || NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
         }
         if (mAppRow.bubblePreference == BUBBLE_PREFERENCE_NONE) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return true;
+        return AVAILABLE;
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/notification/app/AddToHomeScreenPreferenceController.java b/src/com/android/settings/notification/app/AddToHomeScreenPreferenceController.java
index e5afd9d..12b8075 100644
--- a/src/com/android/settings/notification/app/AddToHomeScreenPreferenceController.java
+++ b/src/com/android/settings/notification/app/AddToHomeScreenPreferenceController.java
@@ -32,7 +32,7 @@
     private static final String KEY = "add_to_home";
 
     public AddToHomeScreenPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -41,11 +41,11 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mConversationInfo != null;
+        return mConversationInfo != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/AllowSoundPreferenceController.java b/src/com/android/settings/notification/app/AllowSoundPreferenceController.java
index 0664c54..99d0873 100644
--- a/src/com/android/settings/notification/app/AllowSoundPreferenceController.java
+++ b/src/com/android/settings/notification/app/AllowSoundPreferenceController.java
@@ -40,7 +40,7 @@
     public AllowSoundPreferenceController(Context context,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_IMPORTANCE);
         mDependentFieldListener = dependentFieldListener;
     }
 
@@ -50,11 +50,14 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mChannel != null && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+        if (mChannel != null && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
 
     }
 
diff --git a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
index 92cd911..6c2c0c3 100644
--- a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
@@ -64,7 +64,7 @@
     public AppChannelsBypassingDndPreferenceController(
             Context context,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -110,8 +110,8 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        return mAppRow != null;
+    public int getAvailabilityStatus() {
+        return mAppRow != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
index dd44a13..e7b2378 100644
--- a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
@@ -49,7 +49,12 @@
     protected PreferenceCategory mPreference;
 
     public AppConversationListPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        this(context, backend, KEY);
+    }
+
+    public AppConversationListPreferenceController(Context context, NotificationBackend backend,
+            String key) {
+        super(context, backend, key);
     }
 
     @Override
@@ -58,21 +63,24 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
                     || NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
         }
-        return mBackend.hasSentValidMsg(mAppRow.pkg, mAppRow.uid) || mBackend.isInInvalidMsgState(
-                mAppRow.pkg, mAppRow.uid);
+        if (mBackend.hasSentValidMsg(mAppRow.pkg, mAppRow.uid) || mBackend.isInInvalidMsgState(
+                mAppRow.pkg, mAppRow.uid)) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/AppLinkPreferenceController.java b/src/com/android/settings/notification/app/AppLinkPreferenceController.java
index 043ae69..ecf9670 100644
--- a/src/com/android/settings/notification/app/AppLinkPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppLinkPreferenceController.java
@@ -32,7 +32,7 @@
     private static final String KEY_APP_LINK = "app_link";
 
     public AppLinkPreferenceController(Context context) {
-        super(context, null);
+        super(context, null, KEY_APP_LINK);
     }
 
     @Override
@@ -41,11 +41,11 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mAppRow.settingsIntent != null;
+        return mAppRow.settingsIntent != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BadgePreferenceController.java b/src/com/android/settings/notification/app/BadgePreferenceController.java
index 108fa1d..f94dfb5 100644
--- a/src/com/android/settings/notification/app/BadgePreferenceController.java
+++ b/src/com/android/settings/notification/app/BadgePreferenceController.java
@@ -38,7 +38,7 @@
 
     public BadgePreferenceController(Context context,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_BADGE);
     }
 
     @Override
@@ -47,25 +47,29 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow == null && mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (Settings.Secure.getInt(mContext.getContentResolver(),
                 NOTIFICATION_BADGING, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (isDefaultChannel()) {
-                return true;
+                return AVAILABLE;
             } else {
-                return mAppRow == null ? false : mAppRow.showBadge;
+                return mAppRow == null
+                        ? CONDITIONALLY_UNAVAILABLE
+                        : mAppRow.showBadge
+                                ? AVAILABLE
+                                : CONDITIONALLY_UNAVAILABLE;
             }
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BlockPreferenceController.java b/src/com/android/settings/notification/app/BlockPreferenceController.java
index f4e2132..ea3eaeb 100644
--- a/src/com/android/settings/notification/app/BlockPreferenceController.java
+++ b/src/com/android/settings/notification/app/BlockPreferenceController.java
@@ -42,7 +42,7 @@
     public BlockPreferenceController(Context context,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_BLOCK);
         mDependentFieldListener = dependentFieldListener;
     }
 
@@ -52,14 +52,14 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mPreferenceFilter != null && !isIncludedInFilter()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BubbleCategoryPreferenceController.java b/src/com/android/settings/notification/app/BubbleCategoryPreferenceController.java
index ad3a10c..da67afe 100644
--- a/src/com/android/settings/notification/app/BubbleCategoryPreferenceController.java
+++ b/src/com/android/settings/notification/app/BubbleCategoryPreferenceController.java
@@ -32,15 +32,15 @@
     static final int ON = 1;
 
     public BubbleCategoryPreferenceController(Context context) {
-        super(context, null);
+        super(context, null, KEY);
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return areBubblesEnabled();
+        return areBubblesEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BubbleLinkPreferenceController.java b/src/com/android/settings/notification/app/BubbleLinkPreferenceController.java
index 0b9529b..ed1c9b5 100644
--- a/src/com/android/settings/notification/app/BubbleLinkPreferenceController.java
+++ b/src/com/android/settings/notification/app/BubbleLinkPreferenceController.java
@@ -32,15 +32,15 @@
     static final int ON = 1;
 
     public BubbleLinkPreferenceController(Context context) {
-        super(context, null);
+        super(context, null, KEY);
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return areBubblesEnabled();
+        return areBubblesEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BubblePreferenceController.java b/src/com/android/settings/notification/app/BubblePreferenceController.java
index 351b463..516a45e 100644
--- a/src/com/android/settings/notification/app/BubblePreferenceController.java
+++ b/src/com/android/settings/notification/app/BubblePreferenceController.java
@@ -56,7 +56,7 @@
     public BubblePreferenceController(Context context, @Nullable FragmentManager fragmentManager,
             NotificationBackend backend, boolean isAppPage,
             @Nullable NotificationSettings.DependentFieldListener listener) {
-        super(context, backend);
+        super(context, backend, KEY);
         mFragmentManager = fragmentManager;
         mIsAppPage = isAppPage;
         mListener = listener;
@@ -68,21 +68,24 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (!mIsAppPage && !isEnabled()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (isDefaultChannel()) {
-                return true;
+                return AVAILABLE;
             } else {
-                return mAppRow != null &&  mAppRow.bubblePreference != BUBBLE_PREFERENCE_NONE;
+                if (mAppRow != null &&  mAppRow.bubblePreference != BUBBLE_PREFERENCE_NONE) {
+                    return AVAILABLE;
+                }
+                return CONDITIONALLY_UNAVAILABLE;
             }
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
index 51370b1..abbe89e 100644
--- a/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
+++ b/src/com/android/settings/notification/app/BubbleSummaryPreferenceController.java
@@ -42,28 +42,31 @@
     static final int ON = 1;
 
     public BubbleSummaryPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (!isGloballyEnabled()) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
             if (isDefaultChannel()) {
-                return true;
+                return AVAILABLE;
             } else {
-                return mAppRow != null;
+                return mAppRow != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
             }
         }
-        return isGloballyEnabled() && mBackend.hasSentValidBubble(mAppRow.pkg, mAppRow.uid);
+        if (isGloballyEnabled() && mBackend.hasSentValidBubble(mAppRow.pkg, mAppRow.uid)) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/ChannelListPreferenceController.java b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
index 8db3b21..8d07911 100644
--- a/src/com/android/settings/notification/app/ChannelListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Slog;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -39,16 +40,19 @@
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.PrimarySwitchPreference;
 import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-public class ChannelListPreferenceController extends NotificationPreferenceController {
+public class ChannelListPreferenceController extends NotificationPreferenceController
+        implements BasePreferenceController.UiBlocker {
 
     private static final String KEY = "channels";
     private static final String KEY_GENERAL_CATEGORY = "categories";
@@ -59,7 +63,7 @@
     private PreferenceCategory mPreference;
 
     public ChannelListPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -68,20 +72,20 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null) {
             if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
                     || NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
@@ -91,24 +95,17 @@
 
     @Override
     public void updateState(Preference preference) {
-        mPreference = (PreferenceCategory) preference;
-        // Load channel settings
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... unused) {
+            mPreference = (PreferenceCategory) preference;
+            // Load channel settings
+            ThreadUtils.postOnBackgroundThread(() -> {
                 mChannelGroupList = mBackend.getGroups(mAppRow.pkg, mAppRow.uid).getList();
                 Collections.sort(mChannelGroupList, CHANNEL_GROUP_COMPARATOR);
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(Void unused) {
-                if (mContext == null) {
-                    return;
-                }
+                ThreadUtils.getUiThreadHandler().getLooper().prepare();
                 updateFullList(mPreference, mChannelGroupList);
-            }
-        }.execute();
+                ThreadUtils.postOnMainThread(() -> {
+                    showPreferences();
+                });
+            });
     }
 
     /**
@@ -144,6 +141,12 @@
         }
     }
 
+    private void showPreferences() {
+        if (mUiBlockListener != null) {
+           mUiBlockListener.onBlockerWorkFinished(this);
+        }
+    }
+
     /**
      * Looks for the category for the given group's key at the expected index, if that doesn't
      * match, it checks all groups, and if it can't find that group anywhere, it creates it.
diff --git a/src/com/android/settings/notification/app/ConversationDemotePreferenceController.java b/src/com/android/settings/notification/app/ConversationDemotePreferenceController.java
index 02f639c..ba7ca35 100644
--- a/src/com/android/settings/notification/app/ConversationDemotePreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationDemotePreferenceController.java
@@ -37,7 +37,7 @@
     public ConversationDemotePreferenceController(Context context,
             SettingsPreferenceFragment hostFragment,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
         mHostFragment = hostFragment;
     }
 
@@ -47,14 +47,17 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow == null || mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return !TextUtils.isEmpty(mChannel.getConversationId()) && !mChannel.isDemoted();
+        if (!TextUtils.isEmpty(mChannel.getConversationId()) && !mChannel.isDemoted()) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
index f99a56a..56de88b 100644
--- a/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationHeaderPreferenceController.java
@@ -45,7 +45,7 @@
     private boolean mStarted = false;
 
     public ConversationHeaderPreferenceController(Context context, DashboardFragment fragment) {
-        super(context, null);
+        super(context, null, PREF_KEY_APP_HEADER);
         mFragment = fragment;
     }
 
@@ -55,8 +55,8 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        return mAppRow != null;
+    public int getAvailabilityStatus() {
+        return mAppRow != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/ConversationPriorityPreferenceController.java b/src/com/android/settings/notification/app/ConversationPriorityPreferenceController.java
index ae16928..46bc3c0 100644
--- a/src/com/android/settings/notification/app/ConversationPriorityPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationPriorityPreferenceController.java
@@ -34,7 +34,7 @@
 
     public ConversationPriorityPreferenceController(Context context,
             NotificationBackend backend, NotificationSettings.DependentFieldListener listener) {
-        super(context, backend);
+        super(context, backend, KEY);
         mDependentFieldListener = listener;
     }
 
@@ -44,14 +44,14 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow == null || mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/ConversationPromotePreferenceController.java b/src/com/android/settings/notification/app/ConversationPromotePreferenceController.java
index 24c3d2f..a5ef569 100644
--- a/src/com/android/settings/notification/app/ConversationPromotePreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationPromotePreferenceController.java
@@ -37,7 +37,7 @@
     public ConversationPromotePreferenceController(Context context,
             SettingsPreferenceFragment hostFragment,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
         mHostFragment = hostFragment;
     }
 
@@ -47,14 +47,17 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow == null || mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return !TextUtils.isEmpty(mChannel.getConversationId()) && mChannel.isDemoted();
+        if (!TextUtils.isEmpty(mChannel.getConversationId()) && mChannel.isDemoted()) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java b/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
index 77a692f..cd160df 100644
--- a/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
+++ b/src/com/android/settings/notification/app/DeletedChannelsPreferenceController.java
@@ -30,7 +30,7 @@
     private static final String  KEY_DELETED = "deleted";
 
     public DeletedChannelsPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_DELETED);
     }
 
     @Override
@@ -39,16 +39,19 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         // only visible on app screen
         if (mChannel != null || hasValidGroup()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
 
-        return mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid) > 0;
+        if (mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid) > 0) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/DescriptionPreferenceController.java b/src/com/android/settings/notification/app/DescriptionPreferenceController.java
index 0a5bb2f..413a876 100644
--- a/src/com/android/settings/notification/app/DescriptionPreferenceController.java
+++ b/src/com/android/settings/notification/app/DescriptionPreferenceController.java
@@ -29,7 +29,7 @@
     private static final String KEY_DESC = "desc";
 
     public DescriptionPreferenceController(Context context) {
-        super(context, null);
+        super(context, null, KEY_DESC);
     }
 
     @Override
@@ -38,20 +38,20 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null && !hasValidGroup()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel != null && !TextUtils.isEmpty(mChannel.getDescription())) {
-            return true;
+            return AVAILABLE;
         }
         if (hasValidGroup() && !TextUtils.isEmpty(mChannelGroup.getDescription())) {
-            return true;
+            return AVAILABLE;
         }
-        return false;
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/DndPreferenceController.java b/src/com/android/settings/notification/app/DndPreferenceController.java
index b65928a..811eeb4 100644
--- a/src/com/android/settings/notification/app/DndPreferenceController.java
+++ b/src/com/android/settings/notification/app/DndPreferenceController.java
@@ -31,7 +31,7 @@
     private static final String KEY_BYPASS_DND = "bypass_dnd";
 
     public DndPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_BYPASS_DND);
     }
 
     @Override
@@ -40,11 +40,11 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable() || mChannel == null) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE || mChannel == null) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return true;
+        return AVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/HeaderPreferenceController.java b/src/com/android/settings/notification/app/HeaderPreferenceController.java
index 7379d55..c4b0e59 100644
--- a/src/com/android/settings/notification/app/HeaderPreferenceController.java
+++ b/src/com/android/settings/notification/app/HeaderPreferenceController.java
@@ -45,7 +45,7 @@
     private boolean mStarted = false;
 
     public HeaderPreferenceController(Context context, DashboardFragment fragment) {
-        super(context, null);
+        super(context, null, PREF_KEY_APP_HEADER);
         mFragment = fragment;
     }
 
@@ -55,8 +55,8 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        return mAppRow != null;
+    public int getAvailabilityStatus() {
+        return mAppRow != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/HighImportancePreferenceController.java b/src/com/android/settings/notification/app/HighImportancePreferenceController.java
index d60668b..98dc8a7 100644
--- a/src/com/android/settings/notification/app/HighImportancePreferenceController.java
+++ b/src/com/android/settings/notification/app/HighImportancePreferenceController.java
@@ -37,7 +37,7 @@
     public HighImportancePreferenceController(Context context,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_IMPORTANCE);
         mDependentFieldListener = dependentFieldListener;
     }
 
@@ -47,17 +47,19 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (isDefaultChannel()) {
-           return false;
+           return CONDITIONALLY_UNAVAILABLE;
         }
-        return mChannel.getImportance() >= IMPORTANCE_DEFAULT;
+        return mChannel.getImportance() >= IMPORTANCE_DEFAULT
+                ? AVAILABLE
+                : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/ImportancePreferenceController.java b/src/com/android/settings/notification/app/ImportancePreferenceController.java
index 3c32ca4..31ddac3 100644
--- a/src/com/android/settings/notification/app/ImportancePreferenceController.java
+++ b/src/com/android/settings/notification/app/ImportancePreferenceController.java
@@ -38,7 +38,7 @@
     public ImportancePreferenceController(Context context,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_IMPORTANCE);
         mDependentFieldListener = dependentFieldListener;
     }
 
@@ -48,14 +48,14 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return !isDefaultChannel();
+        return !isDefaultChannel() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java b/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java
index b937e80..bb2c58b 100644
--- a/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java
+++ b/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java
@@ -31,7 +31,7 @@
 
     public InvalidConversationInfoPreferenceController(Context context,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -40,17 +40,19 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mPreferenceFilter != null && !isIncludedInFilter()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
+        return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid)
+                ? AVAILABLE
+                : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java b/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java
index 5c502dc..219ccbc 100644
--- a/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java
+++ b/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java
@@ -31,7 +31,7 @@
     private static final String KEY = "invalid_conversation_switch";
 
     public InvalidConversationPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY);
     }
 
     @Override
@@ -40,17 +40,19 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mPreferenceFilter != null && !isIncludedInFilter()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
+        return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid)
+                ? AVAILABLE
+                : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/LightsPreferenceController.java b/src/com/android/settings/notification/app/LightsPreferenceController.java
index d096922..f7f9244 100644
--- a/src/com/android/settings/notification/app/LightsPreferenceController.java
+++ b/src/com/android/settings/notification/app/LightsPreferenceController.java
@@ -33,7 +33,7 @@
     private static final String KEY_LIGHTS = "lights";
 
     public LightsPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_LIGHTS);
     }
 
     @Override
@@ -42,16 +42,18 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
-                && canPulseLight()
-                && !isDefaultChannel();
+        if (checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight()
+                && !isDefaultChannel()) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/MinImportancePreferenceController.java b/src/com/android/settings/notification/app/MinImportancePreferenceController.java
index f825763..b2c0862 100644
--- a/src/com/android/settings/notification/app/MinImportancePreferenceController.java
+++ b/src/com/android/settings/notification/app/MinImportancePreferenceController.java
@@ -37,7 +37,7 @@
     public MinImportancePreferenceController(Context context,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_IMPORTANCE);
         mDependentFieldListener = dependentFieldListener;
     }
 
@@ -47,17 +47,17 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (isDefaultChannel()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return mChannel.getImportance() <= IMPORTANCE_LOW;
+        return mChannel.getImportance() <= IMPORTANCE_LOW ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/NotificationPreferenceController.java b/src/com/android/settings/notification/app/NotificationPreferenceController.java
index fb19d9d..271a83d 100644
--- a/src/com/android/settings/notification/app/NotificationPreferenceController.java
+++ b/src/com/android/settings/notification/app/NotificationPreferenceController.java
@@ -17,26 +17,24 @@
 package com.android.settings.notification.app;
 
 import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.os.UserHandle.USER_SYSTEM;
 
 import android.annotation.Nullable;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.util.Log;
+import android.util.Slog;
 
 import androidx.preference.Preference;
 
+import com.android.settings.core.BasePreferenceController;
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.Comparator;
 import java.util.List;
@@ -46,7 +44,7 @@
  * Parent class for preferences appearing on notification setting pages at the app,
  * notification channel group, or notification channel level.
  */
-public abstract class NotificationPreferenceController extends AbstractPreferenceController {
+public abstract class NotificationPreferenceController extends BasePreferenceController {
     private static final String TAG = "ChannelPrefContr";
     @Nullable
     protected NotificationChannel mChannel;
@@ -71,8 +69,11 @@
     boolean overrideCanBlockValue;
     boolean overrideCanConfigureValue;
 
-    public NotificationPreferenceController(Context context, NotificationBackend backend) {
-        super(context);
+    boolean mLoadedChannelState;
+
+    public NotificationPreferenceController(Context context, NotificationBackend backend,
+            String key) {
+        super(context, key);
         mContext = context;
         mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mBackend = backend;
@@ -81,28 +82,30 @@
     }
 
     /**
-     * Returns true if field's parent object is not blocked.
+     * Returns available if field's parent object is not blocked.
      */
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannelGroup != null) {
             if (mChannelGroup.isBlocked()) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
         }
         if (mChannel != null) {
             if (mPreferenceFilter != null && !isIncludedInFilter()) {
-                return false;
+                return CONDITIONALLY_UNAVAILABLE;
             }
-            return mChannel.getImportance() != IMPORTANCE_NONE;
+            if(mChannel.getImportance() == IMPORTANCE_NONE) {
+                return CONDITIONALLY_UNAVAILABLE;
+            }
         }
-        return true;
+        return AVAILABLE;
     }
 
     protected void onResume(NotificationBackend.AppRow appRow,
diff --git a/src/com/android/settings/notification/app/NotificationSettings.java b/src/com/android/settings/notification/app/NotificationSettings.java
index 192a0ee..5750167 100644
--- a/src/com/android/settings/notification/app/NotificationSettings.java
+++ b/src/com/android/settings/notification/app/NotificationSettings.java
@@ -41,6 +41,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -129,15 +130,52 @@
             }
         }
 
+        mUserId = UserHandle.getUserId(mUid);
         mPkgInfo = findPackageInfo(mPkg, mUid);
+    }
 
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (mIntent == null && mArgs == null) {
+            toastAndFinish("no intent");
+            return;
+        }
+
+        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
+            toastAndFinish("Missing package or uid or packageinfo");
+            return;
+        }
+
+        startListeningToPackageRemove();
+    }
+
+    @Override
+    public void onDestroy() {
+        stopListeningToPackageRemove();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
+            toastAndFinish("Missing package or uid or packageinfo");
+            return;
+        }
+        mPkgInfo = findPackageInfo(mPkg, mUid);
         if (mPkgInfo != null) {
-            mUserId = UserHandle.getUserId(mUid);
             mSuspendedAppsAdmin = RestrictedLockUtilsInternal.checkIfApplicationIsSuspended(
                     mContext, mPkg, mUserId);
 
-            loadChannel();
             loadAppRow();
+            if (mAppRow == null) {
+                toastAndFinish("Can't load package");
+                return;
+            }
+            loadChannel();
+            loadConversation();
             loadChannelGroup();
             loadPreferencesFilter();
             collectConfigActivities();
@@ -157,55 +195,6 @@
         }
     }
 
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (mIntent == null && mArgs == null) {
-            Log.w(TAG, "No intent");
-            toastAndFinish();
-            return;
-        }
-
-        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null) {
-            Log.w(TAG, "Missing package or uid or packageinfo");
-            toastAndFinish();
-            return;
-        }
-
-        startListeningToPackageRemove();
-    }
-
-    @Override
-    public void onDestroy() {
-        stopListeningToPackageRemove();
-        super.onDestroy();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mAppRow == null) {
-            Log.w(TAG, "Missing package or uid or packageinfo");
-            finish();
-            return;
-        }
-        // Reload app, channel, etc onResume in case they've changed. A little wasteful if we've
-        // just done onAttach but better than making every preference controller reload all
-        // the data
-        loadAppRow();
-        if (mAppRow == null) {
-            Log.w(TAG, "Can't load package");
-            finish();
-            return;
-        }
-        loadChannel();
-        loadConversation();
-        loadChannelGroup();
-        loadPreferencesFilter();
-        collectConfigActivities();
-    }
-
     protected void animatePanel() {
         if (mPreferenceFilter != null) {
             mLayoutView = getActivity().findViewById(R.id.main_content);
@@ -307,7 +296,8 @@
         }
     }
 
-    protected void toastAndFinish() {
+    protected void toastAndFinish(String msg) {
+        Log.w(TAG, msg);
         Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
         getActivity().finish();
     }
diff --git a/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java b/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
index 0c7cd23..46625c3 100644
--- a/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
+++ b/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
@@ -30,7 +30,7 @@
     private static final String KEY_BLOCKED_DESC = "block_desc";
 
     public NotificationsOffPreferenceController(Context context) {
-        super(context, null);
+        super(context, null, KEY_BLOCKED_DESC);
     }
 
     @Override
@@ -39,16 +39,20 @@
     }
 
     @Override
-    public boolean isAvailable() {
+    public int getAvailabilityStatus() {
         if (mAppRow == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mPreferenceFilter != null && !isIncludedInFilter()) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
         // Available only when other controllers are unavailable - this UI replaces the UI that
         // would give more detailed notification controls.
-        return !super.isAvailable();
+        if (super.getAvailabilityStatus() == AVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
+        } else {
+            return AVAILABLE;
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/SoundPreferenceController.java b/src/com/android/settings/notification/app/SoundPreferenceController.java
index b23b4fc..335b442 100644
--- a/src/com/android/settings/notification/app/SoundPreferenceController.java
+++ b/src/com/android/settings/notification/app/SoundPreferenceController.java
@@ -47,7 +47,7 @@
     public SoundPreferenceController(Context context, SettingsPreferenceFragment hostFragment,
             NotificationSettings.DependentFieldListener dependentFieldListener,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_SOUND);
         mFragment = hostFragment;
         mListener = dependentFieldListener;
     }
@@ -58,14 +58,17 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && !isDefaultChannel();
+        if (checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && !isDefaultChannel()) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/VibrationPreferenceController.java b/src/com/android/settings/notification/app/VibrationPreferenceController.java
index 34d1a54..f91999f 100644
--- a/src/com/android/settings/notification/app/VibrationPreferenceController.java
+++ b/src/com/android/settings/notification/app/VibrationPreferenceController.java
@@ -34,7 +34,7 @@
     private final Vibrator mVibrator;
 
     public VibrationPreferenceController(Context context, NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_VIBRATE);
         mVibrator = context.getSystemService(Vibrator.class);
     }
 
@@ -44,14 +44,15 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable() || mChannel == null) {
-            return false;
-       }
-        return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
-                && !isDefaultChannel()
-                && mVibrator != null
-                && mVibrator.hasVibrator();
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE || mChannel == null) {
+            return CONDITIONALLY_UNAVAILABLE;
+        }
+        if (checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && !isDefaultChannel()
+                && mVibrator != null && mVibrator.hasVibrator()) {
+            return AVAILABLE;
+        }
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/notification/app/VisibilityPreferenceController.java b/src/com/android/settings/notification/app/VisibilityPreferenceController.java
index a2a1d76..3f33267 100644
--- a/src/com/android/settings/notification/app/VisibilityPreferenceController.java
+++ b/src/com/android/settings/notification/app/VisibilityPreferenceController.java
@@ -48,7 +48,7 @@
 
     public VisibilityPreferenceController(Context context, LockPatternUtils utils,
             NotificationBackend backend) {
-        super(context, backend);
+        super(context, backend, KEY_VISIBILITY_OVERRIDE);
         mLockPatternUtils = utils;
     }
 
@@ -58,14 +58,18 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        if (!super.isAvailable()) {
-            return false;
+    public int getAvailabilityStatus() {
+        if (super.getAvailabilityStatus() == CONDITIONALLY_UNAVAILABLE) {
+            return CONDITIONALLY_UNAVAILABLE;
         }
         if (mChannel == null || mAppRow.banned) {
-            return false;
+            return CONDITIONALLY_UNAVAILABLE;
         }
-        return checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure();
+        if (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure()) {
+            return AVAILABLE;
+        }
+
+        return CONDITIONALLY_UNAVAILABLE;
     }
 
     @Override
diff --git a/src/com/android/settings/users/AutoSyncWorkDataPreferenceController.java b/src/com/android/settings/users/AutoSyncWorkDataPreferenceController.java
index fb57173..9ac1b87 100644
--- a/src/com/android/settings/users/AutoSyncWorkDataPreferenceController.java
+++ b/src/com/android/settings/users/AutoSyncWorkDataPreferenceController.java
@@ -22,7 +22,8 @@
 
 import com.android.settings.Utils;
 
-public class AutoSyncWorkDataPreferenceController extends AutoSyncPersonalDataPreferenceController {
+/** An account sync data preference controller for work */
+public class AutoSyncWorkDataPreferenceController extends AutoSyncDataPreferenceController {
 
     private static final String KEY_AUTO_SYNC_WORK_ACCOUNT = "auto_sync_work_account_data";
 
@@ -38,6 +39,7 @@
 
     @Override
     public boolean isAvailable() {
+        mUserHandle = Utils.getManagedProfileWithDisabled(mUserManager);
         return mUserHandle != null && !mUserManager.isManagedProfile() && !mUserManager.isLinkedUser()
                 && mUserManager.getProfiles(UserHandle.myUserId()).size() > 1;
     }
diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java
index 8a46c9d..e3e77e8 100644
--- a/src/com/android/settings/wifi/WifiDialogActivity.java
+++ b/src/com/android/settings/wifi/WifiDialogActivity.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
 
+import android.app.KeyguardManager;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -51,6 +52,7 @@
 import com.google.android.setupcompat.util.WizardManagerHelper;
 import com.google.android.setupdesign.util.ThemeHelper;
 
+import java.lang.ref.WeakReference;
 import java.time.Clock;
 import java.time.ZoneOffset;
 
@@ -96,10 +98,12 @@
     // Interval between initiating NetworkDetailsTracker scans.
     private static final long SCAN_INTERVAL_MILLIS = 10_000;
 
-    private WifiDialog mDialog;
+    @VisibleForTesting
+    WifiDialog mDialog;
     private AccessPoint mAccessPoint;
 
-    private WifiDialog2 mDialog2;
+    @VisibleForTesting
+    WifiDialog2 mDialog2;
 
     // The received intent supports a key of WifiTrackerLib or SettingsLib.
     private boolean mIsWifiTrackerLib;
@@ -108,6 +112,7 @@
     private NetworkDetailsTracker mNetworkDetailsTracker;
     private HandlerThread mWorkerThread;
     private WifiManager mWifiManager;
+    private LockScreenMonitor mLockScreenMonitor;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -184,6 +189,10 @@
                 mDialog.setOnDismissListener(this);
             }
         }
+
+        if (mDialog2 != null || mDialog != null) {
+            mLockScreenMonitor = new LockScreenMonitor(this);
+        }
     }
 
     @VisibleForTesting
@@ -221,6 +230,10 @@
             }
         }
 
+        if (mLockScreenMonitor != null) {
+            mLockScreenMonitor.release();
+            mLockScreenMonitor = null;
+        }
         super.onDestroy();
     }
 
@@ -411,4 +424,45 @@
         }
         return false;
     }
+
+    void dismissDialog() {
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+        if (mDialog2 != null) {
+            mDialog2.dismiss();
+            mDialog2 = null;
+        }
+    }
+
+    @VisibleForTesting
+    static final class LockScreenMonitor implements KeyguardManager.KeyguardLockedStateListener {
+        private final WeakReference<WifiDialogActivity> mWifiDialogActivity;
+        private KeyguardManager mKeyguardManager;
+
+        LockScreenMonitor(WifiDialogActivity activity) {
+            mWifiDialogActivity = new WeakReference<>(activity);
+            mKeyguardManager = activity.getSystemService(KeyguardManager.class);
+            mKeyguardManager.addKeyguardLockedStateListener(activity.getMainExecutor(), this);
+        }
+
+        void release() {
+            if (mKeyguardManager == null) return;
+            mKeyguardManager.removeKeyguardLockedStateListener(this);
+            mKeyguardManager = null;
+        }
+
+        @Override
+        public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
+            if (!isKeyguardLocked) return;
+            WifiDialogActivity activity = mWifiDialogActivity.get();
+            if (activity == null) return;
+            activity.dismissDialog();
+
+            Log.e(TAG, "Dismiss Wi-Fi dialog to prevent leaking user data on lock screen!");
+            EventLog.writeEvent(0x534e4554, "231583603", -1 /* UID */,
+                    "Leak Wi-Fi dialog on lock screen");
+        }
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
index b22b156..89e00e1 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
@@ -45,7 +45,7 @@
 
 import com.android.settings.AccessiblePreferenceCategory;
 import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.testutils.shadow.ShadowAccountManager;
 import com.android.settings.testutils.shadow.ShadowContentResolver;
@@ -77,7 +77,7 @@
     @Mock(answer = RETURNS_DEEP_STUBS)
     private UserManager mUserManager;
     @Mock(answer = RETURNS_DEEP_STUBS)
-    private SettingsPreferenceFragment mFragment;
+    private DashboardFragment mFragment;
     @Mock(answer = RETURNS_DEEP_STUBS)
     private AccountManager mAccountManager;
     @Mock(answer = RETURNS_DEEP_STUBS)
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index 894a6c5..fd6689a 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -298,6 +298,14 @@
     }
 
     @Test
+    public void forceUpdatePreferences_prefKeyNull_shouldNotCrash() {
+        mTestFragment.addPreferenceController(new TestPreferenceController(mContext));
+
+        // Should not crash
+        mTestFragment.forceUpdatePreferences();
+    }
+
+    @Test
     public void checkUiBlocker_noUiBlocker_controllerIsNull() {
         mTestFragment.mBlockerController = null;
         mControllers.add(new TestPreferenceController(mContext));
diff --git a/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
index 1b8148b..c7357ad 100644
--- a/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/DatePreferenceControllerTest.java
@@ -18,15 +18,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.DatePickerDialog;
 import android.app.time.Capabilities;
 import android.app.time.TimeCapabilities;
 import android.app.time.TimeCapabilitiesAndConfig;
 import android.app.time.TimeConfiguration;
 import android.app.time.TimeManager;
 import android.app.timedetector.TimeDetector;
+import android.app.timedetector.TimeDetectorHelper;
 import android.content.Context;
 import android.os.UserHandle;
 
@@ -40,17 +44,20 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
 @RunWith(RobolectricTestRunner.class)
 public class DatePreferenceControllerTest {
 
     @Mock
     private Context mContext;
     @Mock
-    private TimeDetector mTimeDetector;
+    private DatePreferenceController.DatePreferenceHost mHost;
     @Mock
     private TimeManager mTimeManager;
     @Mock
-    private DatePreferenceController.DatePreferenceHost mHost;
+    private TimeDetector mTimeDetector;
 
     private RestrictedPreference mPreference;
     private DatePreferenceController mController;
@@ -114,6 +121,26 @@
         verify(mHost).showDatePicker();
     }
 
+    @Test
+    public void testBuildDatePicker() {
+        TimeDetectorHelper timeDetectorHelper = mock(TimeDetectorHelper.class);
+        when(timeDetectorHelper.getManualDateSelectionYearMin()).thenReturn(2015);
+        when(timeDetectorHelper.getManualDateSelectionYearMax()).thenReturn(2020);
+
+        Context context = RuntimeEnvironment.application;
+        DatePickerDialog dialog = mController.buildDatePicker(context, timeDetectorHelper);
+
+        GregorianCalendar calendar = new GregorianCalendar();
+
+        long minDate = dialog.getDatePicker().getMinDate();
+        calendar.setTimeInMillis(minDate);
+        assertEquals(2015, calendar.get(Calendar.YEAR));
+
+        long maxDate = dialog.getDatePicker().getMaxDate();
+        calendar.setTimeInMillis(maxDate);
+        assertEquals(2020, calendar.get(Calendar.YEAR));
+    }
+
     private static TimeCapabilitiesAndConfig createCapabilitiesAndConfig(
             boolean suggestManualAllowed) {
         int suggestManualCapability = suggestManualAllowed ? Capabilities.CAPABILITY_POSSESSED
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java
index 1b5ded0..1fec92a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java
@@ -61,6 +61,18 @@
     }
 
     @Test
+    public void testUpdateState_isSystemOrDefaultAppAndOptimizeStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(
+                BatteryOptimizeUtils.MODE_OPTIMIZED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
     public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() {
         when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
         when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java
index bacfb1a..944376c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedPreferenceControllerTest.java
@@ -69,7 +69,19 @@
     }
 
     @Test
-    public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
+    public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(
+                BatteryOptimizeUtils.MODE_RESTRICTED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() {
         when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
         when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java
index d5500fa..c5642df 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java
@@ -70,13 +70,26 @@
     }
 
     @Test
-    public void testUpdateState_isSystemOrDefaultApp_prefChecked() {
+    public void testUpdateState_isSystemOrDefaultAppAndUnrestrictedStates_prefChecked() {
+        when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
+        when(mockBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(
+                BatteryOptimizeUtils.MODE_UNRESTRICTED);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() {
         when(mockBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
         when(mockBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
 
         mController.updateState(mPreference);
 
-        assertThat(mPreference.isChecked()).isTrue();
+        assertThat(mPreference.isChecked()).isFalse();
+        assertThat(mPreference.isEnabled()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
index 111019f..34ba2eb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
@@ -26,6 +26,7 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
 import android.os.LocaleList;
+import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -55,6 +56,8 @@
     private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
     @Mock
     private AccessibilityManager mMockAccessibilityManager;
+    @Mock
+    private View mMockView;
 
     @Before
     public void setUp() {
@@ -74,13 +77,13 @@
     }
 
     @Test
-    public void testIsAccessibilityEnabled_disable_returnFalse() {
+    public void isAccessibilityEnabled_disable_returnFalse() {
         doReturn(false).when(mMockAccessibilityManager).isEnabled();
         assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isFalse();
     }
 
     @Test
-    public void testIsAccessibilityEnabled_emptyInfo_returnFalse() {
+    public void isAccessibilityEnabled_emptyInfo_returnFalse() {
         doReturn(true).when(mMockAccessibilityManager).isEnabled();
         doReturn(new ArrayList<AccessibilityServiceInfo>())
                 .when(mMockAccessibilityManager)
@@ -90,45 +93,41 @@
     }
 
     @Test
-    public void testIsAccessibilityEnabled_validServiceId_returnTrue() {
+    public void isAccessibilityEnabled_validServiceId_returnTrue() {
         doReturn(true).when(mMockAccessibilityManager).isEnabled();
         assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isTrue();
     }
 
     @Test
-    public void testSetSelectedIndex_invokesCallback() {
+    public void onClick_invokesCallback() {
+        mBatteryChartView.setLevels(new int[] {90, 80, 70, 60});
+        for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) {
+            mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartViewV2.TrapezoidSlot();
+            mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
+            mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f;
+        }
+        mBatteryChartView.mSelectedIndex = 2;
         final int[] selectedIndex = new int[1];
-        final int expectedIndex = 2;
-        mBatteryChartView.mSelectedIndex = 1;
         mBatteryChartView.setOnSelectListener(
                 trapezoidIndex -> {
                     selectedIndex[0] = trapezoidIndex;
                 });
 
-        mBatteryChartView.setSelectedIndex(expectedIndex);
+        // Verify onClick() a different index 1.
+        mBatteryChartView.mTouchUpEventX = 1;
+        selectedIndex[0] = Integer.MIN_VALUE;
+        mBatteryChartView.onClick(mMockView);
+        assertThat(selectedIndex[0]).isEqualTo(1);
 
-        assertThat(mBatteryChartView.mSelectedIndex)
-                .isEqualTo(expectedIndex);
-        assertThat(selectedIndex[0]).isEqualTo(expectedIndex);
+        // Verify onClick() the same index 2.
+        mBatteryChartView.mTouchUpEventX = 2;
+        selectedIndex[0] = Integer.MIN_VALUE;
+        mBatteryChartView.onClick(mMockView);
+        assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewV2.SELECTED_INDEX_ALL);
     }
 
     @Test
-    public void testSetSelectedIndex_sameIndex_notInvokesCallback() {
-        final int[] selectedIndex = new int[1];
-        final int expectedIndex = 1;
-        mBatteryChartView.mSelectedIndex = expectedIndex;
-        mBatteryChartView.setOnSelectListener(
-                trapezoidIndex -> {
-                    selectedIndex[0] = trapezoidIndex;
-                });
-
-        mBatteryChartView.setSelectedIndex(expectedIndex);
-
-        assertThat(selectedIndex[0]).isNotEqualTo(expectedIndex);
-    }
-
-    @Test
-    public void testClickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
+    public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
         mBatteryChartView.setClickableForce(true);
         when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
                 .thenReturn(false);
@@ -139,7 +138,7 @@
     }
 
     @Test
-    public void testClickable_accessibilityIsDisabled_clickable() {
+    public void clickable_accessibilityIsDisabled_clickable() {
         mBatteryChartView.setClickableForce(true);
         when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
                 .thenReturn(true);
@@ -151,7 +150,7 @@
     }
 
     @Test
-    public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() {
+    public void clickable_accessibilityIsEnabledWithoutValidId_clickable() {
         mBatteryChartView.setClickableForce(true);
         when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
                 .thenReturn(true);
@@ -166,7 +165,7 @@
     }
 
     @Test
-    public void testClickable_accessibilityIsEnabledWithValidId_notClickable() {
+    public void clickable_accessibilityIsEnabledWithValidId_notClickable() {
         mBatteryChartView.setClickableForce(true);
         when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
                 .thenReturn(true);
@@ -178,12 +177,11 @@
     }
 
     @Test
-    public void testClickable_restoreFromNonClickableState() {
+    public void clickable_restoreFromNonClickableState() {
         final int[] levels = new int[13];
         for (int index = 0; index < levels.length; index++) {
             levels[index] = index + 1;
         }
-        mBatteryChartView.setTrapezoidCount(12);
         mBatteryChartView.setLevels(levels);
         mBatteryChartView.setClickableForce(true);
         when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
@@ -201,14 +199,14 @@
     }
 
     @Test
-    public void testOnAttachedToWindow_addAccessibilityStateChangeListener() {
+    public void onAttachedToWindow_addAccessibilityStateChangeListener() {
         mBatteryChartView.onAttachedToWindow();
         verify(mMockAccessibilityManager)
                 .addAccessibilityStateChangeListener(mBatteryChartView);
     }
 
     @Test
-    public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() {
+    public void onDetachedFromWindow_removeAccessibilityStateChangeListener() {
         mBatteryChartView.onAttachedToWindow();
         mBatteryChartView.mHandler.postDelayed(
                 mBatteryChartView.mUpdateClickableStateRun, 1000);
@@ -223,7 +221,7 @@
     }
 
     @Test
-    public void testOnAccessibilityStateChanged_postUpdateStateRunnable() {
+    public void onAccessibilityStateChanged_postUpdateStateRunnable() {
         mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
         mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
 
diff --git a/tests/robotests/src/com/android/settings/notification/app/NotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/NotificationPreferenceControllerTest.java
index b2f1673..bf529e6 100644
--- a/tests/robotests/src/com/android/settings/notification/app/NotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/NotificationPreferenceControllerTest.java
@@ -426,7 +426,7 @@
     private final class TestPreferenceController extends NotificationPreferenceController {
 
         private TestPreferenceController(Context context, NotificationBackend backend) {
-            super(context, backend);
+            super(context, backend, "key");
         }
 
         @Override
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index 28b7ecb..c9cc02e 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.KeyguardManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.wifi.WifiConfiguration;
@@ -46,7 +47,6 @@
 import com.google.android.setupcompat.util.WizardManagerHelper;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -82,6 +82,8 @@
     Intent mResultData;
     @Mock
     WifiConfigController mController;
+    @Mock
+    KeyguardManager mKeyguardManager;
 
     WifiDialogActivity mActivity;
 
@@ -99,6 +101,7 @@
         mActivity = spy(Robolectric.setupActivity(WifiDialogActivity.class));
         when(mActivity.getSystemService(UserManager.class)).thenReturn(mUserManager);
         when(mActivity.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
+        when(mActivity.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
     }
 
     @Test
@@ -294,4 +297,35 @@
 
         assertThat(result).isTrue();
     }
+
+    @Test
+    public void dismissDialog_hasDialog_dialogDismiss() {
+        mActivity.mDialog = mWifiDialog;
+        mActivity.mDialog2 = mWifiDialog2;
+
+        mActivity.dismissDialog();
+
+        verify(mWifiDialog).dismiss();
+        verify(mWifiDialog2).dismiss();
+    }
+
+    @Test
+    public void onKeyguardLockedStateChanged_keyguardIsNotLocked_doNotDismissDialog() {
+        WifiDialogActivity.LockScreenMonitor lockScreenMonitor =
+                new WifiDialogActivity.LockScreenMonitor(mActivity);
+
+        lockScreenMonitor.onKeyguardLockedStateChanged(false /* isKeyguardLocked */);
+
+        verify(mActivity, never()).dismissDialog();
+    }
+
+    @Test
+    public void onKeyguardLockedStateChanged_keyguardIsLocked_dismissDialog() {
+        WifiDialogActivity.LockScreenMonitor lockScreenMonitor =
+                new WifiDialogActivity.LockScreenMonitor(mActivity);
+
+        lockScreenMonitor.onKeyguardLockedStateChanged(true /* isKeyguardLocked */);
+
+        verify(mActivity).dismissDialog();
+    }
 }