Merge "Remove accidental log statement from ActivityRecord" into tm-qpr-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0fd80c5..7f48e39 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6965,8 +6965,10 @@
     /**
      * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
      * permission. The permission is checked when a notification is enqueued.
+     *
+     * @hide
      */
-    private boolean hasColorizedPermission() {
+    public boolean hasColorizedPermission() {
         return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
     }
 
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 32cf0a7..56592ab 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1189,8 +1189,8 @@
             PackageManager packageManager = context.getPackageManager();
 
             try {
-                return packageManager.getProperty(context.getOpPackageName(),
-                            PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+                return packageManager.getProperty(PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT,
+                        context.getOpPackageName()).getBoolean();
             } catch (PackageManager.NameNotFoundException e) {
                 // No such property
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 10e1633..6b25c81 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8001,6 +8001,15 @@
                 "accessibility_display_inversion_enabled";
 
         /**
+         * Flag that specifies whether font size has been changed. The flag will
+         * be set when users change the scaled value of font size for the first time.
+         * @hide
+         */
+        @Readable
+        public static final String ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED =
+                "accessibility_font_scaling_has_been_changed";
+
+        /**
          * Setting that specifies whether display color space adjustment is
          * enabled.
          *
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 556636dd..d443270 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -89,6 +89,8 @@
         // Setting for accessibility magnification for following typing.
         optional SettingProto accessibility_magnification_follow_typing_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto contrast_level = 44 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Settings for font scaling
+        optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dafa0ad..993fcf8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6092,4 +6092,8 @@
 
     <!-- Whether to show weather on the lock screen by default. -->
     <bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
+
+    <!-- Whether we should persist the brightness value in nits for the default display even if
+         the underlying display device changes. -->
+    <bool name="config_persistBrightnessNitsForDefaultDisplay">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 591ba5f..c04c322 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2282,6 +2282,7 @@
   <java-symbol type="bool" name="config_preventImeStartupUnlessTextEditor" />
   <java-symbol type="array" name="config_nonPreemptibleInputMethods" />
   <java-symbol type="bool" name="config_enhancedConfirmationModeEnabled" />
+  <java-symbol type="bool" name="config_persistBrightnessNitsForDefaultDisplay" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9224b3c..6b7ca42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -193,6 +193,7 @@
         private final DragDetector mDragDetector;
 
         private int mDragPointerId = -1;
+        private boolean mIsDragging;
 
         private CaptionTouchEventListener(
                 RunningTaskInfo taskInfo,
@@ -223,19 +224,15 @@
             if (v.getId() != R.id.caption) {
                 return false;
             }
-            mDragDetector.onMotionEvent(e);
-
-            if (e.getAction() != MotionEvent.ACTION_DOWN) {
-                return false;
+            if (e.getAction() == MotionEvent.ACTION_DOWN) {
+                final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+                if (!taskInfo.isFocused) {
+                    final WindowContainerTransaction wct = new WindowContainerTransaction();
+                    wct.reorder(mTaskToken, true /* onTop */);
+                    mSyncQueue.queue(wct);
+                }
             }
-            final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            if (taskInfo.isFocused) {
-                return false;
-            }
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.reorder(mTaskToken, true /* onTop */);
-            mSyncQueue.queue(wct);
-            return true;
+            return mDragDetector.onMotionEvent(e);
         }
 
         /**
@@ -253,20 +250,24 @@
                     mDragPointerId = e.getPointerId(0);
                     mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
-                    break;
+                    mIsDragging = false;
+                    return false;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    mIsDragging = true;
+                    return true;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    final boolean wasDragging = mIsDragging;
+                    mIsDragging = false;
+                    return wasDragging;
                 }
             }
             return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c517f7b..dee5f8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -223,6 +223,7 @@
         private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
 
+        private boolean mIsDragging;
         private int mDragPointerId = -1;
 
         private DesktopModeTouchEventListener(
@@ -273,23 +274,7 @@
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption) {
                 return false;
             }
-            switch (e.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    mDragDetector.onMotionEvent(e);
-                    final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-                    if (taskInfo.isFocused) {
-                        return mDragDetector.isDragEvent();
-                    }
-                    return false;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    boolean res = mDragDetector.isDragEvent();
-                    mDragDetector.onMotionEvent(e);
-                    return res;
-                default:
-                    mDragDetector.onMotionEvent(e);
-                    return mDragDetector.isDragEvent();
-            }
+            return mDragDetector.onMotionEvent(e);
         }
 
         /**
@@ -313,13 +298,15 @@
                     mDragPointerId = e.getPointerId(0);
                     mDragPositioningCallback.onDragPositioningStart(
                             0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
-                    break;
+                    mIsDragging = false;
+                    return false;
                 }
                 case MotionEvent.ACTION_MOVE: {
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
-                    break;
+                    mIsDragging = true;
+                    return true;
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
@@ -336,7 +323,9 @@
                                     c -> c.moveToFullscreen(taskInfo));
                         }
                     }
-                    break;
+                    final boolean wasDragging = mIsDragging;
+                    mIsDragging = false;
+                    return wasDragging;
                 }
             }
             return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index cf1850b..65b5a7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -56,10 +56,15 @@
      * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
     */
     boolean onMotionEvent(MotionEvent ev) {
+        final boolean isTouchScreen =
+                (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+        if (!isTouchScreen) {
+            // Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
+            // to take the slop threshold into consideration.
+            return mEventHandler.handleMotionEvent(ev);
+        }
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
-                // Only touch screens generate noisy moves.
-                mIsDragEvent = (ev.getSource() & SOURCE_TOUCHSCREEN) != SOURCE_TOUCHSCREEN;
                 mDragPointerId = ev.getPointerId(0);
                 float rawX = ev.getRawX(0);
                 float rawY = ev.getRawY(0);
@@ -72,8 +77,12 @@
                     int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
                     float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
                     float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
+                    // Touches generate noisy moves, so only once the move is past the touch
+                    // slop threshold should it be considered a drag.
                     mIsDragEvent = Math.hypot(dx, dy) > mTouchSlop;
                 }
+                // The event handler should only be notified about 'move' events if a drag has been
+                // detected.
                 if (mIsDragEvent) {
                     return mEventHandler.handleMotionEvent(ev);
                 } else {
@@ -94,10 +103,6 @@
         mTouchSlop = touchSlop;
     }
 
-    boolean isDragEvent() {
-        return mIsDragEvent;
-    }
-
     private void resetState() {
         mIsDragEvent = false;
         mInputDownPoint.set(0, 0);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index b5eaa4b..6a07dc6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -221,6 +221,7 @@
         Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO,
         Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE,
         Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME,
-        Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED
+        Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
+        Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 534e31a..dd9b4845 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -355,5 +355,6 @@
         VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_CODE, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.LOCK_SCREEN_WEATHER_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a78faaf..c90b95a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1822,6 +1822,9 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
                 SecureSettingsProto.Accessibility
                         .ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
+                SecureSettingsProto.Accessibility.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED);
         p.end(accessibilityToken);
 
         final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index b9f1666..cf5ccc5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.management
 
+import android.annotation.WorkerThread
 import android.content.ComponentName
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.util.UserAwareController
@@ -33,6 +34,9 @@
      */
     fun getCurrentServices(): List<ControlsServiceInfo>
 
+    @WorkerThread
+    fun forceReload()
+
     /**
      * Get the app label for a given component.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index c81a2c7..8ba060e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -16,8 +16,11 @@
 
 package com.android.systemui.controls.management
 
+import android.annotation.WorkerThread
 import android.content.ComponentName
 import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
 import android.util.Log
@@ -65,7 +68,7 @@
     private val serviceListingBuilder: (Context) -> ServiceListing,
     private val userTracker: UserTracker,
     dumpManager: DumpManager,
-    featureFlags: FeatureFlags
+    private val featureFlags: FeatureFlags
 ) : ControlsListingController, Dumpable {
 
     @Inject
@@ -97,18 +100,7 @@
         // After here, `list` is not captured, so we don't risk modifying it outside of the callback
         backgroundExecutor.execute {
             if (userChangeInProgress.get() > 0) return@execute
-            if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
-                val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
-                newServices.forEach {
-                    it.resolvePanelActivity(allowAllApps) }
-            }
-
-            if (newServices != availableServices) {
-                availableServices = newServices
-                callbacks.forEach {
-                    it.onServicesUpdated(getCurrentServices())
-                }
-            }
+            updateServices(newServices)
         }
     }
 
@@ -120,6 +112,21 @@
         serviceListing.reload()
     }
 
+    private fun updateServices(newServices: List<ControlsServiceInfo>) {
+        if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
+            val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
+            newServices.forEach {
+                it.resolvePanelActivity(allowAllApps) }
+        }
+
+        if (newServices != availableServices) {
+            availableServices = newServices
+            callbacks.forEach {
+                it.onServicesUpdated(getCurrentServices())
+            }
+        }
+    }
+
     override fun changeUser(newUser: UserHandle) {
         userChangeInProgress.incrementAndGet()
         serviceListing.setListening(false)
@@ -178,6 +185,23 @@
     override fun getCurrentServices(): List<ControlsServiceInfo> =
             availableServices.map(ControlsServiceInfo::copy)
 
+    @WorkerThread
+    override fun forceReload() {
+        val packageManager = context.packageManager
+        val intent = Intent(ControlsProviderService.SERVICE_CONTROLS)
+        val user = userTracker.userHandle
+        val flags = PackageManager.GET_SERVICES or
+                PackageManager.GET_META_DATA or
+                PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+        val services = packageManager.queryIntentServicesAsUser(
+                intent,
+                PackageManager.ResolveInfoFlags.of(flags.toLong()),
+                user
+        ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) }
+        updateServices(services)
+    }
+
     /**
      * Get the localized label for the component.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
index 3a4a00c..461cacc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -19,6 +19,7 @@
 
 import android.content.Context
 import android.os.UserHandle
+import androidx.annotation.WorkerThread
 import com.android.systemui.CoreStartable
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
@@ -75,11 +76,13 @@
             // Controls is disabled, we don't need this anymore
             return
         }
-        startForUser()
+        executor.execute(this::startForUser)
         userTracker.addCallback(userTrackerCallback, executor)
     }
 
+    @WorkerThread
     private fun startForUser() {
+        controlsListingController.forceReload()
         selectDefaultPanelIfNecessary()
         bindToPanel()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index 24e90f0..aad2090 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -61,9 +61,6 @@
     @VisibleForTesting
     boolean mIsAnimationEnabled;
 
-    // Whether dream entry animations are finished.
-    private boolean mEntryAnimationsFinished = false;
-
     @Inject
     protected ComplicationHostViewController(
             @Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout view,
@@ -78,14 +75,6 @@
         mComplicationCollectionViewModel = viewModel;
         mDreamOverlayStateController = dreamOverlayStateController;
 
-        mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
-            @Override
-            public void onStateChanged() {
-                mEntryAnimationsFinished =
-                        mDreamOverlayStateController.areEntryAnimationsFinished();
-            }
-        });
-
         // Whether animations are enabled.
         mIsAnimationEnabled = secureSettings.getFloatForUser(
                 Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f, UserHandle.USER_CURRENT) != 0.0f;
@@ -159,7 +148,8 @@
 
                     // Complications to be added before dream entry animations are finished are set
                     // to invisible and are animated in.
-                    if (!mEntryAnimationsFinished && mIsAnimationEnabled) {
+                    if (!mDreamOverlayStateController.areEntryAnimationsFinished()
+                            && mIsAnimationEnabled) {
                         view.setVisibility(View.INVISIBLE);
                     }
                     mComplications.put(id, viewHolder);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 999dd48..8b66b00 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -221,6 +221,11 @@
             teamfood = true,
         )
 
+    /** Whether to inflate the bouncer view on a background thread. */
+    // TODO(b/272091103): Tracking Bug
+    @JvmField
+    val ASYNC_INFLATE_BOUNCER = unreleasedFlag(229, "async_inflate_bouncer", teamfood = true)
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 911861d..28cc697 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -64,7 +64,11 @@
                 .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isAbleToDream, lastStartedTransition) = pair
-                    if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) {
+                    if (
+                        isAbleToDream &&
+                            lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
+                            lastStartedTransition.from != KeyguardState.AOD
+                    ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
index 4880f80..b73ddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -59,6 +59,17 @@
         int = value
     }
 
+    /** Updates this to store the same value as [change]. */
+    fun updateTo(change: TableChange) {
+        reset(change.timestamp, change.columnPrefix, change.columnName)
+        when (change.type) {
+            DataType.STRING -> set(change.str)
+            DataType.INT -> set(change.int)
+            DataType.BOOLEAN -> set(change.bool)
+            DataType.EMPTY -> {}
+        }
+    }
+
     /** Returns true if this object has a change. */
     fun hasData(): Boolean {
         return columnName.isNotBlank() && type != DataType.EMPTY
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 29f273a..a0f1c95 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,13 +16,13 @@
 
 package com.android.systemui.log.table
 
+import android.os.Trace
 import com.android.systemui.Dumpable
 import com.android.systemui.plugins.util.RingBuffer
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import java.text.SimpleDateFormat
 import java.util.Locale
-import kotlinx.coroutines.flow.Flow
 
 /**
  * A logger that logs changes in table format.
@@ -82,6 +82,19 @@
 
     private val buffer = RingBuffer(maxSize) { TableChange() }
 
+    // Stores the most recently evicted value for each column name (indexed on column name).
+    //
+    // Why it's necessary: Because we use a RingBuffer of a fixed size, it's possible that a column
+    // that's logged infrequently will eventually get pushed out by a different column that's
+    // logged more frequently. Then, that infrequently-logged column isn't present in the RingBuffer
+    // at all and we have no logs that the column ever existed. This is a problem because the
+    // column's information is still relevant, valid, and may be critical to debugging issues.
+    //
+    // Fix: When a change is being evicted from the RingBuffer, we store it in this map (based on
+    // its [TableChange.getName()]. This ensures that we always have at least one value for every
+    // column ever logged. See b/272016422 for more details.
+    private val lastEvictedValues = mutableMapOf<String, TableChange>()
+
     // A [TableRowLogger] object, re-used each time [logDiffs] is called.
     // (Re-used to avoid object allocation.)
     private val tempRow = TableRowLoggerImpl(0, columnPrefix = "", this)
@@ -138,18 +151,24 @@
     // timestamps.)
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: String?) {
+        Trace.beginSection("TableLogBuffer#logChange(string)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Boolean) {
+        Trace.beginSection("TableLogBuffer#logChange(boolean)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
+        Trace.beginSection("TableLogBuffer#logChange(int)")
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
+        Trace.endSection()
     }
 
     // TODO(b/259454430): Add additional change types here.
@@ -158,6 +177,9 @@
     private fun obtain(timestamp: Long, prefix: String, columnName: String): TableChange {
         verifyValidName(prefix, columnName)
         val tableChange = buffer.advance()
+        if (tableChange.hasData()) {
+            saveEvictedValue(tableChange)
+        }
         tableChange.reset(timestamp, prefix, columnName)
         return tableChange
     }
@@ -173,10 +195,23 @@
         }
     }
 
+    private fun saveEvictedValue(change: TableChange) {
+        Trace.beginSection("TableLogBuffer#saveEvictedValue")
+        val name = change.getName()
+        val previouslyEvicted =
+            lastEvictedValues[name] ?: TableChange().also { lastEvictedValues[name] = it }
+        // For recycling purposes, update the existing object in the map with the new information
+        // instead of creating a new object.
+        previouslyEvicted.updateTo(change)
+        Trace.endSection()
+    }
+
     @Synchronized
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println(HEADER_PREFIX + name)
         pw.println("version $VERSION")
+
+        lastEvictedValues.values.sortedBy { it.timestamp }.forEach { it.dump(pw) }
         for (i in 0 until buffer.size) {
             buffer[i].dump(pw)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index a4de9ff..20b5032 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -76,15 +77,27 @@
     private List<DarkIntensityListener> mDarkIntensityListeners;
 
     private final Handler mHandler = Handler.getMain();
-    private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
-            new IWallpaperVisibilityListener.Stub() {
+
+    static final class WallpaperVisibilityListener extends IWallpaperVisibilityListener.Stub {
+        private final WeakReference<NavigationBarTransitions> mSelf;
+
+        WallpaperVisibilityListener(NavigationBarTransitions self) {
+            mSelf = new WeakReference<>(self);
+        }
+
         @Override
         public void onWallpaperVisibilityChanged(boolean newVisibility,
-        int displayId) throws RemoteException {
-            mWallpaperVisible = newVisibility;
-            mHandler.post(() -> applyLightsOut(true, false));
+                int displayId) throws RemoteException {
+            NavigationBarTransitions self = mSelf.get();
+            if (self == null) {
+                return;
+            }
+            self.mWallpaperVisible = newVisibility;
+            self.mHandler.post(() -> self.applyLightsOut(true, false));
         }
-    };
+    }
+
+    private final IWallpaperVisibilityListener mWallpaperVisibilityListener;
 
     @Inject
     public NavigationBarTransitions(
@@ -93,6 +106,7 @@
             LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
             DisplayTracker displayTracker) {
         super(view, R.drawable.nav_background);
+
         mView = view;
         mWindowManagerService = windowManagerService;
         mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
@@ -101,6 +115,7 @@
                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
         mDarkIntensityListeners = new ArrayList();
 
+        mWallpaperVisibilityListener = new WallpaperVisibilityListener(this);
         try {
             mWallpaperVisible = mWindowManagerService.registerWallpaperVisibilityListener(
                     mWallpaperVisibilityListener, mDisplayTracker.getDefaultDisplayId());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index f335733..c76cfbd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -2,19 +2,15 @@
 
 import android.content.Context
 import android.content.res.Configuration
-import android.content.res.TypedArray
 import android.graphics.Canvas
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.RectF
 import android.util.MathUtils.min
-import android.util.TypedValue
 import android.view.View
-import androidx.appcompat.view.ContextThemeWrapper
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
-import com.android.internal.R.style.Theme_DeviceDefault
 import com.android.internal.util.LatencyTracker
 import com.android.settingslib.Utils
 import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
@@ -159,26 +155,21 @@
         val isDeviceInNightTheme = resources.configuration.uiMode and
                 Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
 
-        val colorControlActivated = ContextThemeWrapper(context, Theme_DeviceDefault)
-                .run {
-                    val typedValue = TypedValue()
-                    val a: TypedArray = obtainStyledAttributes(typedValue.data,
-                            intArrayOf(android.R.attr.colorControlActivated))
-                    val color = a.getColor(0, 0)
-                    a.recycle()
-                    color
-                }
+        arrowPaint.color = Utils.getColorAttrDefaultColor(context,
+            if (isDeviceInNightTheme) {
+                com.android.internal.R.attr.colorAccentPrimary
+            } else {
+                com.android.internal.R.attr.textColorPrimary
+            }
+        )
 
-        val colorPrimary =
-                Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
-
-        arrowPaint.color = Utils.getColorAccentDefaultColor(context)
-
-        arrowBackgroundPaint.color = if (isDeviceInNightTheme) {
-            colorPrimary
-        } else {
-            colorControlActivated
-        }
+        arrowBackgroundPaint.color = Utils.getColorAttrDefaultColor(context,
+            if (isDeviceInNightTheme) {
+                com.android.internal.R.attr.colorSurface
+            } else {
+                com.android.internal.R.attr.colorAccentSecondary
+            }
+        )
     }
 
     inner class AnimatedFloat(
@@ -414,9 +405,9 @@
     ) {
         horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation)
         scale.updateRestingPosition(restingParams.scale)
-        arrowAlpha.updateRestingPosition(restingParams.arrowDimens.alpha)
         backgroundAlpha.updateRestingPosition(restingParams.backgroundDimens.alpha)
 
+        arrowAlpha.updateRestingPosition(restingParams.arrowDimens.alpha, animate)
         arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
         arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
         scalePivotX.updateRestingPosition(restingParams.backgroundDimens.width, animate)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 367d125..ce1c8da 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -55,12 +55,12 @@
 
 internal const val MIN_DURATION_ACTIVE_ANIMATION = 300L
 private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
-private const val MIN_DURATION_COMMITTED_ANIMATION = 200L
+private const val MIN_DURATION_COMMITTED_ANIMATION = 120L
 private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
 private const val MIN_DURATION_CONSIDERED_AS_FLING = 100L
 
 private const val FAILSAFE_DELAY_MS = 350L
-private const val POP_ON_FLING_DELAY = 160L
+private const val POP_ON_FLING_DELAY = 140L
 
 internal val VIBRATE_ACTIVATED_EFFECT =
         VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
@@ -148,8 +148,6 @@
     private var gestureSinceActionDown = 0L
     private var gestureEntryTime = 0L
     private var gestureActiveTime = 0L
-    private var gestureInactiveOrEntryTime = 0L
-    private var gestureArrowStrokeVisibleTime = 0L
 
     private val elapsedTimeSinceActionDown
         get() = SystemClock.uptimeMillis() - gestureSinceActionDown
@@ -441,34 +439,44 @@
 
         updateArrowStateOnMove(yTranslation, xTranslation)
 
-        when (currentState) {
-            GestureState.ACTIVE -> {
-                stretchActiveBackIndicator(fullScreenProgress(xTranslation))
-            }
-            GestureState.ENTRY -> {
-                val progress = staticThresholdProgress(xTranslation)
-                stretchEntryBackIndicator(progress)
-
-                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
-                    mView.popArrowAlpha(0f, it.value)
-                }
-            }
-            GestureState.INACTIVE -> {
-                val progress = reactivationThresholdProgress(totalTouchDelta)
-                stretchInactiveBackIndicator(progress)
-
-                params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
-                    gestureArrowStrokeVisibleTime = SystemClock.uptimeMillis()
-                    mView.popArrowAlpha(0f, it.value)
-                }
-            }
-            else -> {}
+        val gestureProgress = when (currentState) {
+            GestureState.ACTIVE -> fullScreenProgress(xTranslation)
+            GestureState.ENTRY -> staticThresholdProgress(xTranslation)
+            GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDelta)
+            else -> null
         }
 
-        // set y translation
+        gestureProgress?.let {
+            when (currentState) {
+                GestureState.ACTIVE -> stretchActiveBackIndicator(gestureProgress)
+                GestureState.ENTRY -> stretchEntryBackIndicator(gestureProgress)
+                GestureState.INACTIVE -> stretchInactiveBackIndicator(gestureProgress)
+                else -> {}
+            }
+        }
+
+        setArrowStrokeAlpha(gestureProgress)
         setVerticalTranslation(yOffset)
     }
 
+    private fun setArrowStrokeAlpha(gestureProgress: Float?) {
+        val strokeAlphaProgress = when (currentState) {
+            GestureState.ENTRY -> gestureProgress
+            GestureState.INACTIVE -> gestureProgress
+            GestureState.ACTIVE,
+            GestureState.FLUNG,
+            GestureState.COMMITTED -> 1f
+            GestureState.CANCELLED,
+            GestureState.GONE -> 0f
+        }
+
+        strokeAlphaProgress?.let { progress ->
+            params.arrowStrokeAlphaSpring.get(progress).takeIf { it.isNewState }?.let {
+                mView.popArrowAlpha(0f, it.value)
+            }
+        }
+    }
+
     private fun setVerticalTranslation(yOffset: Float) {
         val yTranslation = abs(yOffset)
         val maxYOffset = (mView.height - params.entryIndicator.backgroundDimens.height) / 2f
@@ -599,7 +607,7 @@
 
     private fun isFlungAwayFromEdge(endX: Float, startX: Float = touchDeltaStartX): Boolean {
         val minDistanceConsideredForFling = ViewConfiguration.get(context).scaledTouchSlop
-        val flingDistance = abs(endX - startX)
+        val flingDistance = if (mView.isLeftPanel) endX - startX else startX - endX
         val isPastFlingVelocity = isDragAwayFromEdge(
                 velocityPxPerSecThreshold =
                 ViewConfiguration.get(context).scaledMinimumFlingVelocity)
@@ -764,7 +772,7 @@
                             GestureState.ENTRY,
                             GestureState.INACTIVE -> params.entryIndicator.arrowDimens
                             GestureState.ACTIVE -> params.activeIndicator.arrowDimens
-                            GestureState.FLUNG,
+                            GestureState.FLUNG -> params.flungIndicator.arrowDimens
                             GestureState.COMMITTED -> params.committedIndicator.arrowDimens
                             GestureState.CANCELLED -> params.cancelledIndicator.arrowDimens
                         },
@@ -825,7 +833,6 @@
 
                 updateRestingArrowDimens()
                 gestureEntryTime = SystemClock.uptimeMillis()
-                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
             }
             GestureState.ACTIVE -> {
                 previousXTranslationOnActiveOffset = previousXTranslation
@@ -857,7 +864,13 @@
             }
 
             GestureState.INACTIVE -> {
-                gestureInactiveOrEntryTime = SystemClock.uptimeMillis()
+
+                // Typically entering INACTIVE means
+                // totalTouchDelta <= deactivationSwipeTriggerThreshold
+                // but because we can also independently enter this state
+                // if touch Y >> touch X, we force it to deactivationSwipeTriggerThreshold
+                // so that gesture progress in this state is consistent regardless of entry
+                totalTouchDelta = params.deactivationSwipeTriggerThreshold
 
                 val startingVelocity = convertVelocityToSpringStartingVelocity(
                         valueOnFastVelocity = -1.05f,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 0c00022..d46333a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -9,8 +9,8 @@
 data class EdgePanelParams(private var resources: Resources) {
 
     data class ArrowDimens(
-            val length: Float = 0f,
-            val height: Float = 0f,
+            val length: Float? = 0f,
+            val height: Float? = 0f,
             val alpha: Float = 0f,
             var alphaSpring: SpringForce? = null,
             val heightSpring: SpringForce? = null,
@@ -139,17 +139,17 @@
 
         entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
         entryWidthTowardsEdgeInterpolator = PathInterpolator(1f, -3f, 1f, 1.2f)
-        activeWidthInterpolator = PathInterpolator(.15f, .48f, .46f, .89f)
+        activeWidthInterpolator = PathInterpolator(.32f, 0f, .16f, .94f)
         arrowAngleInterpolator = entryWidthInterpolator
         translationInterpolator = PathInterpolator(0.2f, 1.0f, 1.0f, 1.0f)
         farCornerInterpolator = PathInterpolator(.03f, .19f, .14f, 1.09f)
         edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f)
         heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f)
 
-        val showArrowOnProgressValue = .2f
+        val showArrowOnProgressValue = .23f
         val showArrowOnProgressValueFactor = 1.05f
 
-        val entryActiveHorizontalTranslationSpring = createSpring(675f, 0.8f)
+        val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.8f)
         val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f)
         val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f)
         val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f)
@@ -178,7 +178,7 @@
                         height = getDimen(R.dimen.navigation_edge_entry_background_height),
                         edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
                         farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners),
-                        alphaSpring = createSpring(900f, 1f),
+                        alphaSpring = createSpring(1100f, 1f),
                         widthSpring = createSpring(450f, 0.65f),
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(300f, 0.5f),
@@ -232,7 +232,7 @@
                                 getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
                         farCornerRadius =
                                 getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
-                        widthSpring = createSpring(200f, 0.65f),
+                        widthSpring = createSpring(250f, 0.65f),
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(200f, 1f),
                         edgeCornerRadiusSpring = createSpring(150f, 0.5f),
@@ -244,6 +244,8 @@
                 arrowDimens = activeIndicator.arrowDimens.copy(
                         lengthSpring = activeCommittedArrowLengthSpring,
                         heightSpring = activeCommittedArrowHeightSpring,
+                        length = null,
+                        height = null,
                 ),
                 backgroundDimens = activeIndicator.backgroundDimens.copy(
                         alpha = 0f,
@@ -255,13 +257,15 @@
                         farCornerRadiusSpring = flungCommittedFarCornerSpring,
                 ),
                 scale = 0.85f,
-                scaleSpring = createSpring(650f, 1f),
+                scaleSpring = createSpring(1150f, 1f),
         )
 
         flungIndicator = committedIndicator.copy(
                 arrowDimens = committedIndicator.arrowDimens.copy(
                         lengthSpring = createSpring(850f, 0.46f),
                         heightSpring = createSpring(850f, 0.46f),
+                        length = activeIndicator.arrowDimens.length,
+                        height = activeIndicator.arrowDimens.height
                 ),
                 backgroundDimens = committedIndicator.backgroundDimens.copy(
                         widthSpring = flungCommittedWidthSpring,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b5d51ce..dcd219a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3571,7 +3571,8 @@
         boolean goingToSleepWithoutAnimation = isGoingToSleep()
                 && !mDozeParameters.shouldControlScreenOff();
         boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
-                || goingToSleepWithoutAnimation;
+                || goingToSleepWithoutAnimation
+                || mDeviceProvisionedController.isFrpActive();
         mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
@@ -3745,10 +3746,10 @@
 
         boolean launchingAffordanceWithPreview = mLaunchingAffordance;
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
-
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                    || mTransitionToFullShadeProgress > 0f) {
+            if ((!isOccluded() || isPanelExpanded())
+                    && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f)) {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
             } else {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 3944c8c..0d09fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -21,7 +21,8 @@
 /**
  * Controller to cache in process the state of the device provisioning.
  * <p>
- * This controller keeps track of the values of device provisioning and user setup complete
+ * This controller keeps track of the values of device provisioning, user setup complete, and
+ * whether Factory Reset Protection is active.
  */
 public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
 
@@ -49,6 +50,9 @@
      */
     boolean isCurrentUserSetup();
 
+    /** Returns true when Factory Reset Protection is locking the device. */
+    boolean isFrpActive();
+
     /**
      * Interface to provide calls when the values tracked change
      */
@@ -69,5 +73,10 @@
          * Call when some user changes from not provisioned to provisioned
          */
         default void onUserSetupChanged() { }
+
+        /**
+         * Called when the state of FRP changes.
+         */
+        default void onFrpActiveChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index a6b7d9c5..32c64f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -60,9 +60,11 @@
     }
 
     private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+    private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
     private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
 
     private val deviceProvisioned = AtomicBoolean(false)
+    private val frpActive = AtomicBoolean(false)
     @GuardedBy("lock")
     private val userSetupComplete = SparseBooleanArray()
     @GuardedBy("lock")
@@ -89,11 +91,15 @@
             userId: Int
         ) {
             val updateDeviceProvisioned = deviceProvisionedUri in uris
+            val updateFrp = frpActiveUri in uris
             val updateUser = if (userSetupUri in uris) userId else NO_USERS
-            updateValues(updateDeviceProvisioned, updateUser)
+            updateValues(updateDeviceProvisioned, updateFrp, updateUser)
             if (updateDeviceProvisioned) {
                 onDeviceProvisionedChanged()
             }
+            if (updateFrp) {
+                onFrpActiveChanged()
+            }
             if (updateUser != NO_USERS) {
                 onUserSetupChanged()
             }
@@ -103,7 +109,7 @@
     private val userChangedCallback = object : UserTracker.Callback {
         @WorkerThread
         override fun onUserChanged(newUser: Int, userContext: Context) {
-            updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+            updateValues(updateDeviceProvisioned = false, updateFrp = false, updateUser = newUser)
             onUserSwitched()
         }
 
@@ -125,19 +131,27 @@
         updateValues()
         userTracker.addCallback(userChangedCallback, backgroundExecutor)
         globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+        globalSettings.registerContentObserver(frpActiveUri, observer)
         secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
     }
 
     @WorkerThread
-    private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+    private fun updateValues(
+        updateDeviceProvisioned: Boolean = true,
+        updateFrp: Boolean = true,
+        updateUser: Int = ALL_USERS
+    ) {
         if (updateDeviceProvisioned) {
             deviceProvisioned
                     .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
         }
+        if (updateFrp) {
+            frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+        }
         synchronized(lock) {
             if (updateUser == ALL_USERS) {
-                val N = userSetupComplete.size()
-                for (i in 0 until N) {
+                val n = userSetupComplete.size()
+                for (i in 0 until n) {
                     val user = userSetupComplete.keyAt(i)
                     val value = secureSettings
                             .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
@@ -172,6 +186,10 @@
         return deviceProvisioned.get()
     }
 
+    override fun isFrpActive(): Boolean {
+        return frpActive.get()
+    }
+
     override fun isUserSetup(user: Int): Boolean {
         val index = synchronized(lock) {
             userSetupComplete.indexOfKey(user)
@@ -196,7 +214,13 @@
 
     override fun onDeviceProvisionedChanged() {
         dispatchChange(
-                DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+            DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+        )
+    }
+
+    override fun onFrpActiveChanged() {
+        dispatchChange(
+            DeviceProvisionedController.DeviceProvisionedListener::onFrpActiveChanged
         )
     }
 
@@ -221,6 +245,7 @@
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("Device provisioned: ${deviceProvisioned.get()}")
+        pw.println("Factory Reset Protection active: ${frpActive.get()}")
         synchronized(lock) {
             pw.println("User setup complete: $userSetupComplete")
             pw.println("Listeners: $listeners")
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 064c224..c6da55c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -119,11 +119,11 @@
 
     /**
      * Default constructor for {@link ObservableServiceConnection}.
-     * @param context The context from which the service will be bound with.
+     * @param context       The context from which the service will be bound with.
      * @param serviceIntent The intent to  bind service with.
-     * @param executor The executor for connection callbacks to be delivered on
-     * @param transformer A {@link ServiceTransformer} for transforming the resulting service
-     *                    into a desired type.
+     * @param executor      The executor for connection callbacks to be delivered on
+     * @param transformer   A {@link ServiceTransformer} for transforming the resulting service
+     *                      into a desired type.
      */
     @Inject
     public ObservableServiceConnection(Context context, Intent serviceIntent,
@@ -143,7 +143,13 @@
      * @return {@code true} if initiating binding succeed, {@code false} otherwise.
      */
     public boolean bind() {
-        final boolean bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+        boolean bindResult = false;
+        try {
+            bindResult = mContext.bindService(mServiceIntent, mFlags, mExecutor, this);
+        } catch (SecurityException e) {
+            Log.d(TAG, "Could not bind to service", e);
+            mContext.unbindService(this);
+        }
         mBoundCalled = true;
         if (DEBUG) {
             Log.d(TAG, "bind. bound:" + bindResult);
@@ -197,7 +203,7 @@
             Log.d(TAG, "removeCallback:" + callback);
         }
 
-        mExecutor.execute(()-> mCallbacks.removeIf(el -> el.get() == callback));
+        mExecutor.execute(() -> mCallbacks.removeIf(el-> el.get() == callback));
     }
 
     private void onDisconnected(@DisconnectReason int reason) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 35cd3d2..10bfc1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -45,6 +45,8 @@
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
@@ -62,7 +64,6 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -106,6 +107,7 @@
         MockitoAnnotations.initMocks(this)
 
         `when`(userTracker.userId).thenReturn(user)
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
         `when`(userTracker.userContext).thenReturn(context)
         // Return disabled by default
         `when`(packageManager.getComponentEnabledSetting(any()))
@@ -564,6 +566,79 @@
         assertTrue(controller.getCurrentServices().isEmpty())
     }
 
+    @Test
+    fun testForceReloadQueriesPackageManager() {
+        val user = 10
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
+
+        controller.forceReload()
+        verify(packageManager).queryIntentServicesAsUser(
+                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                argThat(FlagsMatcher(
+                        PackageManager.GET_META_DATA.toLong() or
+                                PackageManager.GET_SERVICES.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                )),
+                eq(UserHandle.of(user))
+        )
+    }
+
+    @Test
+    fun testForceReloadUpdatesList() {
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(packageManager.queryIntentServicesAsUser(
+                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                argThat(FlagsMatcher(
+                        PackageManager.GET_META_DATA.toLong() or
+                                PackageManager.GET_SERVICES.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                )),
+                any<UserHandle>()
+        )).thenReturn(listOf(resolveInfo))
+
+        controller.forceReload()
+
+        val services = controller.getCurrentServices()
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+    @Test
+    fun testForceReloadCallsListeners() {
+        controller.addCallback(mockCallback)
+        executor.runAllReady()
+
+        @Suppress("unchecked_cast")
+        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+                ArgumentCaptor.forClass(List::class.java)
+                        as ArgumentCaptor<List<ControlsServiceInfo>>
+
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(packageManager.queryIntentServicesAsUser(
+                any(),
+                any<PackageManager.ResolveInfoFlags>(),
+                any<UserHandle>()
+        )).thenReturn(listOf(resolveInfo))
+
+        reset(mockCallback)
+        controller.forceReload()
+
+        verify(mockCallback).onServicesUpdated(capture(captor))
+
+        val services = captor.value
+
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+
+
     private fun ServiceInfo(
             componentName: ComponentName,
             panelActivityComponentName: ComponentName? = null
@@ -600,7 +675,7 @@
     private fun setUpQueryResult(infos: List<ActivityInfo>) {
         `when`(
                 packageManager.queryIntentActivitiesAsUser(
-                        argThat(IntentMatcher(activityName)),
+                        argThat(IntentMatcherComponent(activityName)),
                         argThat(FlagsMatcher(FLAGS)),
                         eq(UserHandle.of(user))
                 )
@@ -609,7 +684,7 @@
         })
     }
 
-    private class IntentMatcher(
+    private class IntentMatcherComponent(
             private val componentName: ComponentName
     ) : ArgumentMatcher<Intent> {
         override fun matches(argument: Intent?): Boolean {
@@ -617,6 +692,14 @@
         }
     }
 
+    private class IntentMatcherAction(
+            private val action: String
+    ) : ArgumentMatcher<Intent> {
+        override fun matches(argument: Intent?): Boolean {
+            return argument?.action == action
+        }
+    }
+
     private class FlagsMatcher(
             private val flags: Long
     ) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index 9d8084d..bd7e98e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -42,6 +42,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
@@ -80,9 +82,10 @@
     fun testNoPreferredPackagesNoDefaultSelected_noNewSelection() {
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -92,9 +95,10 @@
         whenever(authorizedPanelsRepository.getPreferredPackages())
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
-        `when`(controlsListingController.getCurrentServices()).thenReturn(emptyList())
+        setUpControlsListingControls(emptyList())
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -105,9 +109,10 @@
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "not panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -119,9 +124,10 @@
         `when`(controlsController.getPreferredSelection())
             .thenReturn(mock<SelectedItem.PanelItem>())
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).setPreferredSelection(any())
     }
@@ -132,9 +138,10 @@
             .thenReturn(setOf(TEST_PACKAGE_PANEL))
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
     }
@@ -149,9 +156,10 @@
                 ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true),
                 ControlsServiceInfo(ComponentName("other_package", "cls"), "non panel", false)
             )
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[0].toPanelItem())
     }
@@ -166,9 +174,10 @@
                 ControlsServiceInfo(ComponentName("other_package", "cls"), "panel", true),
                 ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)
             )
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).setPreferredSelection(listings[1].toPanelItem())
     }
@@ -176,10 +185,11 @@
     @Test
     fun testPreferredSelectionIsPanel_bindOnStart() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection()).thenReturn(listings[0].toPanelItem())
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController).bindComponentForPanel(TEST_COMPONENT_PANEL)
     }
@@ -187,11 +197,12 @@
     @Test
     fun testPreferredSelectionPanel_listingNoPanel_notBind() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection())
             .thenReturn(SelectedItem.PanelItem("panel", TEST_COMPONENT_PANEL))
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).bindComponentForPanel(any())
     }
@@ -199,10 +210,11 @@
     @Test
     fun testNotPanelSelection_noBind() {
         val listings = listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = false))
-        `when`(controlsListingController.getCurrentServices()).thenReturn(listings)
+        setUpControlsListingControls(listings)
         `when`(controlsController.getPreferredSelection()).thenReturn(SelectedItem.EMPTY_SELECTION)
 
         createStartable(enabled = true).start()
+        fakeExecutor.runAllReady()
 
         verify(controlsController, never()).bindComponentForPanel(any())
     }
@@ -221,6 +233,12 @@
         verify(controlsController, never()).setPreferredSelection(any())
     }
 
+    private fun setUpControlsListingControls(listings: List<ControlsServiceInfo>) {
+        doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() }
+            .`when`(controlsListingController)
+            .forceReload()
+    }
+
     private fun createStartable(enabled: Boolean): ControlsStartable {
         val component: ControlsComponent =
             mock() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
index 068852d..95c6897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationHostViewControllerTest.java
@@ -92,9 +92,6 @@
     @Captor
     private ArgumentCaptor<Observer<Collection<ComplicationViewModel>>> mObserverCaptor;
 
-    @Captor
-    private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
-
     @Complication.Category
     static final int COMPLICATION_CATEGORY = Complication.CATEGORY_SYSTEM;
 
@@ -189,8 +186,6 @@
 
         // Dream entry animations finished.
         when(mDreamOverlayStateController.areEntryAnimationsFinished()).thenReturn(true);
-        final DreamOverlayStateController.Callback stateCallback = captureOverlayStateCallback();
-        stateCallback.onStateChanged();
 
         // Add a complication after entry animations are finished.
         final HashSet<ComplicationViewModel> complications = new HashSet<>(
@@ -223,9 +218,4 @@
                 mObserverCaptor.capture());
         return mObserverCaptor.getValue();
     }
-
-    private DreamOverlayStateController.Callback captureOverlayStateCallback() {
-        verify(mDreamOverlayStateController).addCallback(mCallbackCaptor.capture());
-        return mCallbackCaptor.getValue();
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index fc3a638..5cd24e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -358,6 +358,50 @@
         }
 
     @Test
+    fun `LOCKSCREEN to DREAMING`() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            reset(mockTransitionRepository)
+
+            // WHEN the device begins to dream
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun `LOCKSCREEN to DOZING`() =
         testScope.runTest {
             // GIVEN a device with AOD not available
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index c7f3fa0..fb20bac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -121,4 +121,92 @@
         assertThat(underTest.getName()).doesNotContain("original")
         assertThat(underTest.getVal()).isEqualTo("8900")
     }
+
+    @Test
+    fun updateTo_emptyToString_isString() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set("newString")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("newString")
+    }
+
+    @Test
+    fun updateTo_intToEmpty_isEmpty() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(42)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isFalse()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
+    fun updateTo_stringToBool_isBool() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set("oldString")
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(true)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("true")
+    }
+
+    @Test
+    fun updateTo_intToString_isString() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(43)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set("newString")
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("newString")
+    }
+
+    @Test
+    fun updateTo_boolToInt_isInt() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(false)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(44)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("44")
+    }
+
+    @Test
+    fun updateTo_boolToNewBool_isNewBool() {
+        val underTest = TableChange(columnPrefix = "fakePrefix", columnName = "fakeName")
+        underTest.set(false)
+
+        val new = TableChange(columnPrefix = "newPrefix", columnName = "newName")
+        new.set(true)
+        underTest.updateTo(new)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getName()).contains("newPrefix")
+        assertThat(underTest.getName()).contains("newName")
+        assertThat(underTest.getVal()).isEqualTo("true")
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 2c8d7ab..949fa1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -435,11 +435,236 @@
 
         assertThat(dumpedString).doesNotContain("testString[0]")
         assertThat(dumpedString).doesNotContain("testString[1]")
-        assertThat(dumpedString).doesNotContain("testString[2]")
+        // The buffer should contain [MAX_SIZE + 1] entries since we also save the most recently
+        // evicted value.
+        assertThat(dumpedString).contains("testString[2]")
         assertThat(dumpedString).contains("testString[3]")
         assertThat(dumpedString).contains("testString[${MAX_SIZE + 2}]")
     }
 
+    @Test
+    fun columnEvicted_lastKnownColumnValueInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvicted", value = "evictedValue")
+
+        // Exactly fill the buffer so that "willBeEvicted" is evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have both the evicted column entry...
+        val evictedColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvicted" +
+                SEPARATOR +
+                "evictedValue"
+        assertThat(dumpedString).contains(evictedColumnLog)
+
+        // ... *and* all of the fillingColumn entries.
+        val firstFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[0]"
+        val lastFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(1100L) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[9]"
+        assertThat(dumpedString).contains(firstFillingColumnLog)
+        assertThat(dumpedString).contains(lastFillingColumnLog)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_allColumnsInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedString", value = "evictedValue")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedInt", value = 45)
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedBool", value = true)
+
+        // Exactly fill the buffer so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries...
+        val evictedColumnLogString =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvictedString" +
+                SEPARATOR +
+                "evictedValue"
+        val evictedColumnLogInt =
+            TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + "willBeEvictedInt" + SEPARATOR + "45"
+        val evictedColumnLogBool =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "willBeEvictedBool" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(evictedColumnLogString)
+        assertThat(dumpedString).contains(evictedColumnLogInt)
+        assertThat(dumpedString).contains(evictedColumnLogBool)
+
+        // ... *and* all of the fillingColumn entries.
+        val firstFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(400) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[0]"
+        val lastFillingColumnLog =
+            TABLE_LOG_DATE_FORMAT.format(1300) +
+                SEPARATOR +
+                "fillingColumn" +
+                SEPARATOR +
+                "fillString[9]"
+        assertThat(dumpedString).contains(firstFillingColumnLog)
+        assertThat(dumpedString).contains(lastFillingColumnLog)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_differentPrefixSameName_allColumnsInDump() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "prefix1", columnName = "sameName", value = "value1")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "prefix2", columnName = "sameName", value = "value2")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "prefix3", columnName = "sameName", value = "value3")
+
+        // Exactly fill the buffer so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries
+        val evictedColumn1 =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "prefix1.sameName" +
+                SEPARATOR +
+                "value1"
+        val evictedColumn2 =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "prefix2.sameName" +
+                SEPARATOR +
+                "value2"
+        val evictedColumn3 =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "prefix3.sameName" +
+                SEPARATOR +
+                "value3"
+        assertThat(dumpedString).contains(evictedColumn1)
+        assertThat(dumpedString).contains(evictedColumn2)
+        assertThat(dumpedString).contains(evictedColumn3)
+    }
+
+    @Test
+    fun multipleColumnsEvicted_dumpSortedByTimestamp() {
+        systemClock.setCurrentTimeMillis(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedFirst", value = "evictedValue")
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedSecond", value = 45)
+        systemClock.advanceTime(100L)
+        underTest.logChange(prefix = "", columnName = "willBeEvictedThird", value = true)
+
+        // Exactly fill the buffer with so that all the above columns will be evicted
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we'll have all the evicted column entries in timestamp order
+        val firstEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "willBeEvictedFirst" +
+                SEPARATOR +
+                "evictedValue"
+        val secondEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "willBeEvictedSecond" +
+                SEPARATOR +
+                "45"
+        val thirdEvictedLog =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "willBeEvictedThird" +
+                SEPARATOR +
+                "true"
+        assertThat(dumpedString).contains(firstEvictedLog)
+        val stringAfterFirst = dumpedString.substringAfter(firstEvictedLog)
+        assertThat(stringAfterFirst).contains(secondEvictedLog)
+        val stringAfterSecond = stringAfterFirst.substringAfter(secondEvictedLog)
+        assertThat(stringAfterSecond).contains(thirdEvictedLog)
+    }
+
+    @Test
+    fun sameColumnEvictedMultipleTimes_onlyLastEvictionInDump() {
+        systemClock.setCurrentTimeMillis(0L)
+
+        for (i in 1 until 4) {
+            systemClock.advanceTime(100L)
+            val dumpString = "evicted[$i]"
+            underTest.logChange(prefix = "", columnName = "evictedColumn", value = dumpString)
+        }
+
+        // Exactly fill the buffer so that all the entries for "evictedColumn" will be evicted.
+        for (i in 0 until MAX_SIZE) {
+            systemClock.advanceTime(100L)
+            val dumpString = "fillString[$i]"
+            underTest.logChange(prefix = "", columnName = "fillingColumn", value = dumpString)
+        }
+
+        val dumpedString = dumpChanges()
+
+        // Expect that we only have the most recent evicted column entry
+        val evictedColumnLog1 =
+            TABLE_LOG_DATE_FORMAT.format(100L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[1]"
+        val evictedColumnLog2 =
+            TABLE_LOG_DATE_FORMAT.format(200L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[2]"
+        val evictedColumnLog3 =
+            TABLE_LOG_DATE_FORMAT.format(300L) +
+                SEPARATOR +
+                "evictedColumn" +
+                SEPARATOR +
+                "evicted[3]"
+        assertThat(dumpedString).doesNotContain(evictedColumnLog1)
+        assertThat(dumpedString).doesNotContain(evictedColumnLog2)
+        assertThat(dumpedString).contains(evictedColumnLog3)
+    }
+
     private fun dumpChanges(): String {
         underTest.dump(PrintWriter(outputWriter), arrayOf())
         return outputWriter.toString()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5a5b142..98f5a10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -1036,6 +1036,34 @@
     }
 
     @Test
+    public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and panel is NOT expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = false;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED));
+    }
+
+    @Test
+    public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and qs IS expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = true;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+    }
+
+    @Test
     public void testShowKeyguardImplementation_setsState() {
         when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
 
@@ -1259,6 +1287,15 @@
         verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
     }
 
+    @Test
+    public void frpLockedDevice_shadeDisabled() {
+        when(mDeviceProvisionedController.isFrpActive()).thenReturn(true);
+        when(mDozeServiceHost.isPulsing()).thenReturn(true);
+        mCentralSurfaces.updateNotificationPanelTouchState();
+
+        verify(mNotificationPanelViewController).setTouchAndAnimationDisabled(true);
+    }
+
     /**
      * Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
      * to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 5129f85..6980a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -90,6 +90,12 @@
     }
 
     @Test
+    fun testFrpNotActiveByDefault() {
+        init()
+        assertThat(controller.isFrpActive).isFalse()
+    }
+
+    @Test
     fun testNotUserSetupByDefault() {
         init()
         assertThat(controller.isUserSetup(START_USER)).isFalse()
@@ -104,6 +110,14 @@
     }
 
     @Test
+    fun testFrpActiveWhenCreated() {
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        init()
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupWhenCreated() {
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         init()
@@ -122,6 +136,16 @@
     }
 
     @Test
+    fun testFrpActiveChange() {
+        init()
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupChange() {
         init()
 
@@ -164,6 +188,7 @@
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
@@ -181,6 +206,7 @@
         verify(listener).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -195,6 +221,7 @@
         verify(listener, never()).onUserSwitched()
         verify(listener).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -208,10 +235,26 @@
 
         verify(listener, never()).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener).onDeviceProvisionedChanged()
     }
 
     @Test
+    fun testListenerCalledOnFrpActiveChanged() {
+        init()
+        controller.addCallback(listener)
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onUserSwitched()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener).onFrpActiveChanged()
+    }
+
+    @Test
     fun testRemoveListener() {
         init()
         controller.addCallback(listener)
@@ -220,11 +263,13 @@
         switchUser(10)
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
 
         testableLooper.processAllMessages()
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 046ad12..f9bfafc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.util.service;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
@@ -169,4 +171,19 @@
         verify(mCallback).onDisconnected(eq(connection),
                 eq(ObservableServiceConnection.DISCONNECT_REASON_UNBIND));
     }
+
+    @Test
+    public void testBindServiceThrowsError() {
+        ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
+                mIntent, mExecutor, mTransformer);
+        connection.addCallback(mCallback);
+
+        when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
+                .thenThrow(new SecurityException());
+
+        // Verify that the exception was caught and that bind returns false, and we properly
+        // unbind.
+        assertThat(connection.bind()).isFalse();
+        verify(mContext).unbindService(connection);
+    }
 }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 8b579ac..2292d9b 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1127,6 +1127,14 @@
         }
     }
 
+    public float convertToFloatScale(float nits) {
+        if (mCurrentBrightnessMapper != null) {
+            return mCurrentBrightnessMapper.convertToFloatScale(nits);
+        } else {
+            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        }
+    }
+
     public void recalculateSplines(boolean applyAdjustment, float[] adjustment) {
         mCurrentBrightnessMapper.recalculateSplines(applyAdjustment, adjustment);
 
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 3fc50c4..d047183 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -322,6 +322,14 @@
     public abstract float convertToNits(float brightness);
 
     /**
+     * Converts the provided nit value to a float scale value if possible.
+     *
+     * Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for
+     * the nits to float scale.
+     */
+    public abstract float convertToFloatScale(float nits);
+
+    /**
      * Adds a user interaction data point to the brightness mapping.
      *
      * This data point <b>must</b> exist on the brightness curve as a result of this call. This is
@@ -671,6 +679,11 @@
         }
 
         @Override
+        public float convertToFloatScale(float nits) {
+            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        }
+
+        @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
             if (mLoggingEnabled) {
@@ -913,6 +926,11 @@
         }
 
         @Override
+        public float convertToFloatScale(float nits) {
+            return mNitsToBrightnessSpline.interpolate(nits);
+        }
+
+        @Override
         public void addUserDataPoint(float lux, float brightness) {
             float unadjustedBrightness = getUnadjustedBrightness(lux);
             if (mLoggingEnabled) {
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 7448611..9982d2e 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -117,6 +117,23 @@
         }
     }
 
+    /**
+     * @return The brightness for the default display in nits. Used when the underlying display
+     * device has changed but we want to persist the nit value.
+     */
+    float getBrightnessNitsForDefaultDisplay() {
+        return mPersistentDataStore.getBrightnessNitsForDefaultDisplay();
+    }
+
+    /**
+     * Set brightness in nits for the default display. Used when we want to persist the nit value
+     * even if the underlying display device changes.
+     * @param nits The brightness value in nits
+     */
+    void setBrightnessNitsForDefaultDisplay(float nits) {
+        mPersistentDataStore.setBrightnessNitsForDefaultDisplay(nits);
+    }
+
     private void notifyListeners(float brightness) {
         for (BrightnessSettingListener l : mListeners) {
             l.onBrightnessChanged(brightness);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c869fcd..2584800 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -233,6 +233,10 @@
     // True if should use light sensor to automatically determine doze screen brightness.
     private final boolean mAllowAutoBrightnessWhileDozingConfig;
 
+    // True if we want to persist the brightness value in nits even if the underlying display
+    // device changes.
+    private final boolean mPersistBrightnessNitsForDefaultDisplay;
+
     // True if the brightness config has changed and the short-term model needs to be reset
     private boolean mShouldResetShortTermModel;
 
@@ -583,6 +587,9 @@
         mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
 
+        mPersistBrightnessNitsForDefaultDisplay = resources.getBoolean(
+                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay);
+
         mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
                 .getDisplayDeviceConfig();
 
@@ -651,7 +658,7 @@
 
         loadProximitySensor();
 
-        mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+        loadNitBasedBrightnessSetting();
         mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
         mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
         mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
@@ -829,6 +836,7 @@
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 loadFromDisplayDeviceConfig(token, info, hbmMetadata);
+                loadNitBasedBrightnessSetting();
 
                 /// Since the underlying display-device changed, we really don't know the
                 // last command that was sent to change it's state. Lets assume it is unknown so
@@ -2498,10 +2506,33 @@
         return clampScreenBrightnessForVr(brightnessFloat);
     }
 
+    private void loadNitBasedBrightnessSetting() {
+        if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
+            float brightnessNitsForDefaultDisplay =
+                    mBrightnessSetting.getBrightnessNitsForDefaultDisplay();
+            if (brightnessNitsForDefaultDisplay >= 0) {
+                float brightnessForDefaultDisplay = convertToFloatScale(
+                        brightnessNitsForDefaultDisplay);
+                if (isValidBrightnessValue(brightnessForDefaultDisplay)) {
+                    mBrightnessSetting.setBrightness(brightnessForDefaultDisplay);
+                    mCurrentScreenBrightnessSetting = brightnessForDefaultDisplay;
+                    return;
+                }
+            }
+        }
+        mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+    }
+
     void setBrightness(float brightnessValue) {
         // Update the setting, which will eventually call back into DPC to have us actually update
         // the display with the new value.
         mBrightnessSetting.setBrightness(brightnessValue);
+        if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) {
+            float nits = convertToNits(brightnessValue);
+            if (nits >= 0) {
+                mBrightnessSetting.setBrightnessNitsForDefaultDisplay(nits);
+            }
+        }
     }
 
     void onBootCompleted() {
@@ -2514,7 +2545,7 @@
             return;
         }
         setCurrentScreenBrightness(brightnessValue);
-        mBrightnessSetting.setBrightness(brightnessValue);
+        setBrightness(brightnessValue);
     }
 
     private void setCurrentScreenBrightness(float brightnessValue) {
@@ -2593,6 +2624,13 @@
         return mAutomaticBrightnessController.convertToNits(brightness);
     }
 
+    private float convertToFloatScale(float nits) {
+        if (mAutomaticBrightnessController == null) {
+            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        }
+        return mAutomaticBrightnessController.convertToFloatScale(nits);
+    }
+
     @GuardedBy("mLock")
     private void updatePendingProximityRequestsLocked() {
         mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
@@ -2690,25 +2728,27 @@
         pw.println("  mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
         pw.println("  mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
         pw.println("  mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
-        pw.println("  mAllowAutoBrightnessWhileDozingConfig=" +
-                mAllowAutoBrightnessWhileDozingConfig);
+        pw.println("  mAllowAutoBrightnessWhileDozingConfig="
+                + mAllowAutoBrightnessWhileDozingConfig);
+        pw.println("  mPersistBrightnessNitsForDefaultDisplay="
+                + mPersistBrightnessNitsForDefaultDisplay);
         pw.println("  mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
         pw.println("  mColorFadeFadesConfig=" + mColorFadeFadesConfig);
         pw.println("  mColorFadeEnabled=" + mColorFadeEnabled);
         synchronized (mCachedBrightnessInfo) {
-            pw.println("  mCachedBrightnessInfo.brightness=" +
-                    mCachedBrightnessInfo.brightness.value);
-            pw.println("  mCachedBrightnessInfo.adjustedBrightness=" +
-                    mCachedBrightnessInfo.adjustedBrightness.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMin=" +
-                    mCachedBrightnessInfo.brightnessMin.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMax=" +
-                    mCachedBrightnessInfo.brightnessMax.value);
+            pw.println("  mCachedBrightnessInfo.brightness="
+                    + mCachedBrightnessInfo.brightness.value);
+            pw.println("  mCachedBrightnessInfo.adjustedBrightness="
+                    + mCachedBrightnessInfo.adjustedBrightness.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMin="
+                    + mCachedBrightnessInfo.brightnessMin.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMax="
+                    + mCachedBrightnessInfo.brightnessMax.value);
             pw.println("  mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
-            pw.println("  mCachedBrightnessInfo.hbmTransitionPoint=" +
-                    mCachedBrightnessInfo.hbmTransitionPoint.value);
-            pw.println("  mCachedBrightnessInfo.brightnessMaxReason =" +
-                    mCachedBrightnessInfo.brightnessMaxReason.value);
+            pw.println("  mCachedBrightnessInfo.hbmTransitionPoint="
+                    + mCachedBrightnessInfo.hbmTransitionPoint.value);
+            pw.println("  mCachedBrightnessInfo.brightnessMaxReason ="
+                    + mCachedBrightnessInfo.brightnessMaxReason.value);
         }
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 73131a1..a8e0d58 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -94,6 +94,7 @@
  *          &lt;/brightness-curve>
  *      &lt;/brightness-configuration>
  *  &lt;/brightness-configurations>
+ *  &lt;brightness-nits-for-default-display>600&lt;/brightness-nits-for-default-display>
  * &lt;/display-manager-state>
  * </code>
  *
@@ -130,6 +131,9 @@
     private static final String TAG_RESOLUTION_HEIGHT = "resolution-height";
     private static final String TAG_REFRESH_RATE = "refresh-rate";
 
+    private static final String TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY =
+            "brightness-nits-for-default-display";
+
     // Remembered Wifi display devices.
     private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
 
@@ -137,6 +141,8 @@
     private final HashMap<String, DisplayState> mDisplayStates =
             new HashMap<String, DisplayState>();
 
+    private float mBrightnessNitsForDefaultDisplay = -1;
+
     // Display values which should be stable across the device's lifetime.
     private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
 
@@ -312,6 +318,19 @@
         return false;
     }
 
+    public float getBrightnessNitsForDefaultDisplay() {
+        return mBrightnessNitsForDefaultDisplay;
+    }
+
+    public boolean setBrightnessNitsForDefaultDisplay(float nits) {
+        if (nits != mBrightnessNitsForDefaultDisplay) {
+            mBrightnessNitsForDefaultDisplay = nits;
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
     public boolean setUserPreferredRefreshRate(DisplayDevice displayDevice, float refreshRate) {
         final String displayDeviceUniqueId = displayDevice.getUniqueId();
         if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
@@ -513,6 +532,10 @@
             if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
                 mGlobalBrightnessConfigurations.loadFromXml(parser);
             }
+            if (parser.getName().equals(TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY)) {
+                String value = parser.nextText();
+                mBrightnessNitsForDefaultDisplay = Float.parseFloat(value);
+            }
         }
     }
 
@@ -592,6 +615,9 @@
         serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
         mGlobalBrightnessConfigurations.saveToXml(serializer);
         serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
+        serializer.startTag(null, TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY);
+        serializer.text(Float.toString(mBrightnessNitsForDefaultDisplay));
+        serializer.endTag(null, TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY);
         serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
         serializer.endDocument();
     }
@@ -615,6 +641,7 @@
         mStableDeviceValues.dump(pw, "      ");
         pw.println("  GlobalBrightnessConfigurations:");
         mGlobalBrightnessConfigurations.dump(pw, "      ");
+        pw.println("  mBrightnessNitsForDefaultDisplay=" + mBrightnessNitsForDefaultDisplay);
     }
 
     private static final class DisplayState {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ff10cbc..d114139 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
@@ -6323,21 +6324,40 @@
             checkCallerIsSystem();
             mHandler.post(() -> {
                 synchronized (mNotificationLock) {
-                    // strip flag from all enqueued notifications. listeners will be informed
-                    // in post runnable.
-                    List<NotificationRecord> enqueued = findNotificationsByListLocked(
-                            mEnqueuedNotifications, pkg, null, notificationId, userId);
-                    for (int i = 0; i < enqueued.size(); i++) {
-                        removeForegroundServiceFlagLocked(enqueued.get(i));
+                    int count = getNotificationCount(pkg, userId);
+                    boolean removeFgsNotification = false;
+                    if (count > MAX_PACKAGE_NOTIFICATIONS) {
+                        mUsageStats.registerOverCountQuota(pkg);
+                        removeFgsNotification = true;
                     }
+                    if (removeFgsNotification) {
+                        NotificationRecord r = findNotificationLocked(pkg, null, notificationId,
+                                userId);
+                        if (r != null) {
+                            if (DBG) {
+                                Slog.d(TAG, "Remove FGS flag not allow. Cancel FGS notification");
+                            }
+                            removeFromNotificationListsLocked(r);
+                            cancelNotificationLocked(r, false, REASON_APP_CANCEL, true,
+                                    null, SystemClock.elapsedRealtime());
+                        }
+                    } else {
+                        // strip flag from all enqueued notifications. listeners will be informed
+                        // in post runnable.
+                        List<NotificationRecord> enqueued = findNotificationsByListLocked(
+                                mEnqueuedNotifications, pkg, null, notificationId, userId);
+                        for (int i = 0; i < enqueued.size(); i++) {
+                            removeForegroundServiceFlagLocked(enqueued.get(i));
+                        }
 
-                    // if posted notification exists, strip its flag and tell listeners
-                    NotificationRecord r = findNotificationByListLocked(
-                            mNotificationList, pkg, null, notificationId, userId);
-                    if (r != null) {
-                        removeForegroundServiceFlagLocked(r);
-                        mRankingHelper.sort(mNotificationList);
-                        mListeners.notifyPostedLocked(r, r);
+                        // if posted notification exists, strip its flag and tell listeners
+                        NotificationRecord r = findNotificationByListLocked(
+                                mNotificationList, pkg, null, notificationId, userId);
+                        if (r != null) {
+                            removeForegroundServiceFlagLocked(r);
+                            mRankingHelper.sort(mNotificationList);
+                            mListeners.notifyPostedLocked(r, r);
+                        }
                     }
                 }
             });
@@ -6483,9 +6503,17 @@
 
         checkRestrictedCategories(notification);
 
+        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
+        // but it's also possible that the app has called notify() with an update to an
+        // FGS notification that hasn't yet been displayed.  Make sure we check for any
+        // FGS-related situation up front, outside of any locks so it's safe to call into
+        // the Activity Manager.
+        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
+                notification, tag, id, pkg, userId);
+
         // Fix the notification as best we can.
         try {
-            fixNotification(notification, pkg, tag, id, userId);
+            fixNotification(notification, pkg, tag, id, userId, notificationUid, policy);
         } catch (Exception e) {
             if (notification.isForegroundService()) {
                 throw new SecurityException("Invalid FGS notification", e);
@@ -6494,13 +6522,7 @@
             return;
         }
 
-        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
-        // but it's also possible that the app has called notify() with an update to an
-        // FGS notification that hasn't yet been displayed.  Make sure we check for any
-        // FGS-related situation up front, outside of any locks so it's safe to call into
-        // the Activity Manager.
-        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
-                notification, tag, id, pkg, userId);
+
         if (policy == ServiceNotificationPolicy.UPDATE_ONLY) {
             // Proceed if the notification is already showing/known, otherwise ignore
             // because the service lifecycle logic has retained responsibility for its
@@ -6663,14 +6685,20 @@
 
     @VisibleForTesting
     protected void fixNotification(Notification notification, String pkg, String tag, int id,
-            int userId) throws NameNotFoundException, RemoteException {
+            @UserIdInt int userId, int notificationUid, ServiceNotificationPolicy fgsPolicy)
+            throws NameNotFoundException, RemoteException {
         final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                 (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
         Notification.addFieldsFromContext(ai, notification);
 
-        int canColorize = mPackageManagerClient.checkPermission(
-                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+        if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
+            notification.flags &= ~FLAG_FOREGROUND_SERVICE;
+        }
+
+        int canColorize = getContext().checkPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, -1, notificationUid);
+
         if (canColorize == PERMISSION_GRANTED) {
             notification.flags |= Notification.FLAG_CAN_COLORIZE;
         } else {
@@ -7059,6 +7087,29 @@
         return mPermissionHelper.hasPermission(uid);
     }
 
+    private int getNotificationCount(String pkg, int userId) {
+        int count = 0;
+        synchronized (mNotificationLock) {
+            final int numListSize = mNotificationList.size();
+            for (int i = 0; i < numListSize; i++) {
+                final NotificationRecord existing = mNotificationList.get(i);
+                if (existing.getSbn().getPackageName().equals(pkg)
+                        && existing.getSbn().getUserId() == userId) {
+                    count++;
+                }
+            }
+            final int numEnqSize = mEnqueuedNotifications.size();
+            for (int i = 0; i < numEnqSize; i++) {
+                final NotificationRecord existing = mEnqueuedNotifications.get(i);
+                if (existing.getSbn().getPackageName().equals(pkg)
+                        && existing.getSbn().getUserId() == userId) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
     protected int getNotificationCount(String pkg, int userId, int excludedId,
             String excludedTag) {
         int count = 0;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 7fc46fd..a2b2983 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1641,7 +1641,7 @@
             return false;
         }
         int uid = injectGetPackageUid(systemChooser.getPackageName(), UserHandle.USER_SYSTEM);
-        return uid == callingUid;
+        return UserHandle.getAppId(uid) == UserHandle.getAppId(callingUid);
     }
 
     private void enforceSystemOrShell() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 52f2b63..41e0fd7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4577,7 +4577,7 @@
 
     void requestUpdateWallpaperIfNeeded() {
         final DisplayContent dc = getDisplayContent();
-        if (dc != null && hasWallpaper()) {
+        if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) {
             dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
             dc.setLayoutNeeded();
             mWmService.mWindowPlacerLocked.requestTraversal();
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 35a677e..817b245 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -377,6 +377,33 @@
         assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice)));
     }
 
+    @Test
+    public void testStoreAndRestoreBrightnessNitsForDefaultDisplay() {
+        float brightnessNitsForDefaultDisplay = 190;
+        mDataStore.loadIfNeeded();
+        mDataStore.setBrightnessNitsForDefaultDisplay(brightnessNitsForDefaultDisplay);
+
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        mInjector.setWriteStream(baos);
+        mDataStore.saveIfNeeded();
+        mTestLooper.dispatchAll();
+        assertTrue(mInjector.wasWriteSuccessful());
+        TestInjector newInjector = new TestInjector();
+        PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        newInjector.setReadStream(bais);
+        newDataStore.loadIfNeeded();
+        assertEquals(brightnessNitsForDefaultDisplay,
+                mDataStore.getBrightnessNitsForDefaultDisplay(), 0);
+        assertEquals(brightnessNitsForDefaultDisplay,
+                newDataStore.getBrightnessNitsForDefaultDisplay(), 0);
+    }
+
+    @Test
+    public void testInitialBrightnessNitsForDefaultDisplay() {
+        mDataStore.loadIfNeeded();
+        assertEquals(-1, mDataStore.getBrightnessNitsForDefaultDisplay(), 0);
+    }
 
     public class TestInjector extends PersistentDataStore.Injector {
         private InputStream mReadStream;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 96e2a09..99a361c 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
+import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_BUBBLE;
@@ -1160,6 +1162,8 @@
     public void testEnqueuedBlockedNotifications_appBlockedChannelForegroundService()
             throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         NotificationChannel channel = new NotificationChannel("blocked", "name",
                 NotificationManager.IMPORTANCE_NONE);
@@ -1182,6 +1186,8 @@
     public void testEnqueuedBlockedNotifications_userBlockedChannelForegroundService()
             throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         NotificationChannel channel =
                 new NotificationChannel("blockedbyuser", "name", IMPORTANCE_HIGH);
@@ -1261,6 +1267,8 @@
     public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -1590,6 +1598,10 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -1618,6 +1630,10 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -1904,6 +1920,8 @@
 
     @Test
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1918,7 +1936,27 @@
     }
 
     @Test
+    public void testCancelAllNotifications_FgsFlag_NoFgs_Allowed() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(NOT_FOREGROUND_SERVICE);
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_IgnoreForegroundService",
+                sbn.getId(), sbn.getNotification(), sbn.getUserId());
+        mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(sbn.getPackageName());
+        assertEquals(0, notifs.length);
+    }
+
+    @Test
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2006,6 +2044,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n =
                 new Notification.Builder(mContext, mTestNotificationChannel.getId())
                         .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -2043,6 +2084,9 @@
     @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2066,6 +2110,9 @@
     @Test
     public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2135,6 +2182,9 @@
     @Test
     public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2160,6 +2210,9 @@
     @Test
     public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         mService.isSystemUid = false;
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -2281,6 +2334,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2304,6 +2360,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -2402,6 +2461,9 @@
     @Test
     public void testCancelNotificationsFromListener_clearAll_Fgs()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
         child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2466,6 +2528,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2491,6 +2556,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -2596,6 +2664,9 @@
     @Test
     public void testCancelNotificationsFromListener_byKey_Fgs()
             throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
         child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2762,6 +2833,9 @@
 
     @Test
     public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         final NotificationRecord child = generateNotificationRecord(
@@ -6199,6 +6273,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlagFromNotification_enqueued() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n = new Notification.Builder(mContext, "").build();
         n.flags |= FLAG_FOREGROUND_SERVICE;
 
@@ -6218,6 +6295,9 @@
 
     @Test
     public void testRemoveForegroundServiceFlagFromNotification_posted() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         Notification n = new Notification.Builder(mContext, "").build();
         n.flags |= FLAG_FOREGROUND_SERVICE;
 
@@ -6240,6 +6320,68 @@
     }
 
     @Test
+    public void testCannotRemoveForegroundFlagWhenOverLimit_enqueued() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
+        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
+            Notification n = new Notification.Builder(mContext, "").build();
+            StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
+                    n, UserHandle.getUserHandleForUid(mUid), null, 0);
+            NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+            mService.addEnqueuedNotification(r);
+        }
+        Notification n = new Notification.Builder(mContext, "").build();
+        n.flags |= FLAG_FOREGROUND_SERVICE;
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG,
+                NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+
+        mInternalService.removeForegroundServiceFlagFromNotification(
+                PKG, r.getSbn().getId(), r.getSbn().getUserId());
+
+        waitForIdle();
+
+        assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS,
+                mService.getNotificationRecordCount());
+    }
+
+    @Test
+    public void testCannotRemoveForegroundFlagWhenOverLimit_posted() {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
+        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
+            Notification n = new Notification.Builder(mContext, "").build();
+            StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
+                    n, UserHandle.getUserHandleForUid(mUid), null, 0);
+            NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+            mService.addNotification(r);
+        }
+        Notification n = new Notification.Builder(mContext, "").build();
+        n.flags |= FLAG_FOREGROUND_SERVICE;
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG,
+                NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS, null, mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addNotification(r);
+
+        mInternalService.removeForegroundServiceFlagFromNotification(
+                PKG, r.getSbn().getId(), r.getSbn().getUserId());
+
+        waitForIdle();
+
+        assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS,
+                mService.getNotificationRecordCount());
+    }
+
+    @Test
     public void testAllowForegroundCustomToasts() throws Exception {
         final String testPackage = "testPackageName";
         assertEquals(0, mService.mToastQueue.size());
@@ -8230,7 +8372,7 @@
         assertNotNull(n.publicVersion.bigContentView);
         assertNotNull(n.publicVersion.headsUpContentView);
 
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
 
         assertNull(n.contentView);
         assertNull(n.bigContentView);
@@ -8921,6 +9063,9 @@
 
     @Test
     public void testCanPostFgsWhenOverLimit() throws RemoteException {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                     i, null, false).getSbn();
@@ -8946,6 +9091,9 @@
 
     @Test
     public void testCannotPostNonFgsWhenOverLimit() throws RemoteException {
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(SHOW_IMMEDIATELY);
         for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                     i, null, false).getSbn();
@@ -8968,6 +9116,17 @@
                 "testCanPostFgsWhenOverLimit - non fgs over limit!",
                 sbn2.getId(), sbn2.getNotification(), sbn2.getUserId());
 
+
+        when(mAmi.applyForegroundServiceNotification(
+                any(), anyString(), anyInt(), anyString(), anyInt()))
+                .thenReturn(NOT_FOREGROUND_SERVICE);
+        final StatusBarNotification sbn3 = generateNotificationRecord(mTestNotificationChannel,
+                101, null, false).getSbn();
+        sbn3.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCanPostFgsWhenOverLimit - fake fgs over limit!",
+                sbn3.getId(), sbn3.getNotification(), sbn3.getUserId());
+
         waitForIdle();
 
         StatusBarNotification[] notifs =
@@ -10025,4 +10184,21 @@
         mInternalService.sendReviewPermissionsNotification();
         verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
     }
+
+    @Test
+    public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(applicationInfo);
+
+        Notification n = new Notification.Builder(mContext, "test")
+                .setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .setFlag(FLAG_CAN_COLORIZE, true)
+                .build();
+
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);
+
+        assertFalse(n.isForegroundService());
+        assertFalse(n.hasColorizedPermission());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 1407cdd..65f31a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -52,6 +52,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
@@ -61,6 +62,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.window.ClientWindowFrames;
 
 import androidx.test.filters.SmallTest;
 
@@ -338,6 +340,29 @@
     }
 
     @Test
+    public void testWallpaperReportConfigChange() {
+        final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
+        createWallpaperTargetWindow(mDisplayContent);
+        final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
+        makeWindowVisible(wallpaperWindow);
+        wallpaperWindow.mLayoutSeq = mDisplayContent.mLayoutSeq;
+        // Assume the token was invisible and the latest config was reported.
+        wallpaperToken.commitVisibility(false);
+        wallpaperWindow.fillClientWindowFramesAndConfiguration(new ClientWindowFrames(),
+                new MergedConfiguration(), true /* useLatestConfig */, false /* relayoutVisible */);
+        assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+
+        final Rect bounds = wallpaperToken.getBounds();
+        wallpaperToken.setBounds(new Rect(0, 0, bounds.width() / 2, bounds.height() / 2));
+        assertFalse(wallpaperWindow.isLastConfigReportedToClient());
+        // If there is a pending config change when changing to visible, it should tell the client
+        // to redraw by WindowState#reportResized.
+        wallpaperToken.commitVisibility(true);
+        waitUntilHandlersIdle();
+        assertTrue(wallpaperWindow.isLastConfigReportedToClient());
+    }
+
+    @Test
     public void testWallpaperTokenVisibility() {
         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
         final WindowState wallpaperWindow = createWallpaperWindow(dc);