Merge "Pass transition token to finish() for Keyguard" into udc-dev
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index d87226c..65d4b43 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -77,18 +77,18 @@
     private static final String TAG = "CameraAdvancedExtensionSessionImpl";
 
     private final Executor mExecutor;
-    private final CameraDevice mCameraDevice;
+    private CameraDevice mCameraDevice;
     private final Map<String, CameraMetadataNative> mCharacteristicsMap;
     private final long mExtensionClientId;
     private final Handler mHandler;
     private final HandlerThread mHandlerThread;
     private final CameraExtensionSession.StateCallback mCallbacks;
-    private final IAdvancedExtenderImpl mAdvancedExtender;
+    private IAdvancedExtenderImpl mAdvancedExtender;
     // maps registered camera surfaces to extension output configs
     private final HashMap<Surface, CameraOutputConfig> mCameraConfigMap = new HashMap<>();
     // maps camera extension output ids to camera registered image readers
     private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
-    private final RequestProcessor mRequestProcessor = new RequestProcessor();
+    private RequestProcessor mRequestProcessor = new RequestProcessor();
     private final int mSessionId;
 
     private Surface mClientRepeatingRequestSurface;
@@ -100,7 +100,7 @@
     private final ExtensionSessionStatsAggregator mStatsAggregator;
 
     private boolean mInitialized;
-
+    private boolean mSessionClosed;
 
     // Lock to synchronize cross-thread access to device public interface
     final Object mInterfaceLock;
@@ -237,6 +237,7 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mInitialized = false;
+        mSessionClosed = false;
         mInitializeHandler = new InitializeSessionHandler();
         mSessionId = sessionId;
         mInterfaceLock = cameraDevice.mInterfaceLock;
@@ -424,7 +425,7 @@
                 mSessionProcessor.setParameters(request);
 
                 seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
-                        executor, listener));
+                        executor, listener, mCameraDevice.getId()));
             } catch (RemoteException e) {
                 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
                         "Failed to enable repeating request, extension service failed to respond!");
@@ -452,7 +453,7 @@
                     mSessionProcessor.setParameters(request);
 
                     seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
-                            executor, listener), isPostviewRequested);
+                            executor, listener, mCameraDevice.getId()), isPostviewRequested);
                 } catch (RemoteException e) {
                     throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
                             " to submit capture request, extension service failed to respond!");
@@ -460,8 +461,8 @@
             } else if ((mClientRepeatingRequestSurface != null) &&
                     request.containsTarget(mClientRepeatingRequestSurface)) {
                 try {
-                    seqId = mSessionProcessor.startTrigger(request,
-                            new RequestCallbackHandler(request, executor, listener));
+                    seqId = mSessionProcessor.startTrigger(request, new RequestCallbackHandler(
+                            request, executor, listener, mCameraDevice.getId()));
                 } catch (RemoteException e) {
                     throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
                             " to submit trigger request, extension service failed to respond!");
@@ -528,6 +529,7 @@
                     mCaptureSession.stopRepeating();
                     mSessionProcessor.stopRepeating();
                     mSessionProcessor.onCaptureSessionEnd();
+                    mSessionClosed = true;
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to stop the repeating request or end the session,"
                             + " , extension service does not respond!") ;
@@ -560,6 +562,9 @@
 
             if (mSessionProcessor != null) {
                 try {
+                    if (!mSessionClosed) {
+                        mSessionProcessor.onCaptureSessionEnd();
+                    }
                     mSessionProcessor.deInitSession();
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to de-initialize session processor, extension service"
@@ -584,6 +589,10 @@
 
             mClientRepeatingRequestSurface = null;
             mClientCaptureSurface = null;
+            mCaptureSession = null;
+            mRequestProcessor = null;
+            mCameraDevice = null;
+            mAdvancedExtender = null;
         }
 
         if (notifyClose && !skipCloseNotification) {
@@ -706,13 +715,16 @@
         private final CaptureRequest mClientRequest;
         private final Executor mClientExecutor;
         private final ExtensionCaptureCallback mClientCallbacks;
+        private final String mCameraId;
 
         private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
                 @NonNull Executor clientExecutor,
-                @NonNull ExtensionCaptureCallback clientCallbacks) {
+                @NonNull ExtensionCaptureCallback clientCallbacks,
+                @NonNull String cameraId) {
             mClientRequest = clientRequest;
             mClientExecutor = clientExecutor;
             mClientCallbacks = clientCallbacks;
+            mCameraId = cameraId;
         }
 
         @Override
@@ -784,7 +796,7 @@
             }
 
             result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
-            TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
+            TotalCaptureResult totalResult = new TotalCaptureResult(mCameraId, result,
                     mClientRequest, requestId, timestamp, new ArrayList<>(), mSessionId,
                     new PhysicalCaptureResultInfo[0]);
             final long ident = Binder.clearCallingIdentity();
@@ -1036,14 +1048,20 @@
         public int submitBurst(List<Request> requests, IRequestCallback callback) {
             int seqId = -1;
             try {
-                CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
-                ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
-                for (Request request : requests) {
-                    captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
-                            mCameraConfigMap));
+                synchronized (mInterfaceLock) {
+                    if (!mInitialized) {
+                        return seqId;
+                    }
+
+                    CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+                    ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
+                    for (Request request : requests) {
+                        captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
+                                mCameraConfigMap));
+                    }
+                    seqId = mCaptureSession.captureBurstRequests(captureRequests,
+                            new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
                 }
-                seqId = mCaptureSession.captureBurstRequests(captureRequests,
-                        new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
             } catch (CameraAccessException e) {
                 Log.e(TAG, "Failed to submit capture requests!");
             } catch (IllegalStateException e) {
@@ -1057,11 +1075,17 @@
         public int setRepeating(Request request, IRequestCallback callback) {
             int seqId = -1;
             try {
-                CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
+                synchronized (mInterfaceLock) {
+                    if (!mInitialized) {
+                        return seqId;
+                    }
+
+                    CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
                             request, mCameraConfigMap);
-                CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
-                seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
-                        new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+                    CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+                    seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
+                            new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+                }
             } catch (CameraAccessException e) {
                 Log.e(TAG, "Failed to enable repeating request!");
             } catch (IllegalStateException e) {
@@ -1074,7 +1098,13 @@
         @Override
         public void abortCaptures() {
             try {
-                mCaptureSession.abortCaptures();
+                synchronized (mInterfaceLock) {
+                    if (!mInitialized) {
+                        return;
+                    }
+
+                    mCaptureSession.abortCaptures();
+                }
             } catch (CameraAccessException e) {
                 Log.e(TAG, "Failed during capture abort!");
             } catch (IllegalStateException e) {
@@ -1085,7 +1115,13 @@
         @Override
         public void stopRepeating() {
             try {
-                mCaptureSession.stopRepeating();
+                synchronized (mInterfaceLock) {
+                    if (!mInitialized) {
+                        return;
+                    }
+
+                    mCaptureSession.stopRepeating();
+                }
             } catch (CameraAccessException e) {
                 Log.e(TAG, "Failed during repeating capture stop!");
             } catch (IllegalStateException e) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index f9aa1bd..1e6e503 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -239,7 +239,7 @@
         // display state, so we have to look through all displays to match the address
         final Display[] displays = mDisplayManager.getDisplays(
                 DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
-        final Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+
 
         for (int i = 0; i < displays.length; i++) {
             DisplayAddress.Physical address =
@@ -255,6 +255,8 @@
                 // TODO(b/287170025): This should be something like if (!rearDisplay.isEnabled)
                 //  instead. Currently when the rear display is disabled, its state is STATE_OFF.
                 if (rearDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) {
+                    final Display defaultDisplay = mDisplayManager
+                            .getDisplay(Display.DEFAULT_DISPLAY);
                     rotateRearDisplayMetricsIfNeeded(defaultDisplay.getRotation(),
                             rearDisplay.getRotation(), rearDisplayMetrics);
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index acbdbf9..cff3172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -710,4 +710,19 @@
     static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
         return new DesktopModeTaskRepository();
     }
+
+    //
+    // Misc
+    //
+
+    // TODO: Temporarily move dependencies to this instead of ShellInit since that is needed to add
+    // the callback. We will be moving to a different explicit startup mechanism in a follow- up CL.
+    @WMSingleton
+    @ShellCreateTriggerOverride
+    @Provides
+    static Object provideIndependentShellComponentsToCreate(
+            DefaultMixedHandler defaultMixedHandler,
+            Optional<DesktopModeController> desktopModeController) {
+        return new Object();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index da14d03..964ba9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2365,6 +2365,11 @@
                     prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
                 }
             }
+
+            // When split in the background, it should be only opening/dismissing transition and
+            // would keep out not empty. Prevent intercepting all transitions for split screen when
+            // it is in the background and not identify to handle it.
+            return (!out.isEmpty() || isSplitScreenVisible()) ? out : null;
         } else {
             if (isOpening && getStageOfTask(triggerTask) != null) {
                 // One task is appearing into split, prepare to enter split screen.
@@ -2373,8 +2378,8 @@
                 mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
                         TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
             }
+            return out;
         }
-        return out;
     }
 
     /**
@@ -2506,8 +2511,9 @@
                         && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN)
                         || mMainStage.getChildCount() == 0 ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
                 // If there is a fullscreen opening change, we should not bring stage to top.
-                prepareExitSplitScreen(record.mContainShowFullscreenChange
-                        ? STAGE_TYPE_UNDEFINED : dismissTop, wct);
+                prepareExitSplitScreen(
+                        !record.mContainShowFullscreenChange && isSplitScreenVisible()
+                        ? dismissTop : STAGE_TYPE_UNDEFINED, wct);
                 mSplitTransitions.startDismissTransition(wct, this, dismissTop,
                         EXIT_REASON_APP_FINISHED);
                 // This can happen in some pathological cases. For example:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 1d7c35d..7a0a24a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -104,7 +104,7 @@
             case PROMPT_REASON_DEVICE_ADMIN:
                 return R.string.kg_prompt_reason_device_admin;
             case PROMPT_REASON_USER_REQUEST:
-                return R.string.kg_prompt_reason_user_request;
+                return R.string.kg_prompt_after_user_lockdown_password;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
                 return R.string.kg_prompt_reason_timeout_password;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 64b1c50..be42376 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -311,7 +311,7 @@
                 resId = R.string.kg_prompt_reason_device_admin;
                 break;
             case PROMPT_REASON_USER_REQUEST:
-                resId = R.string.kg_prompt_reason_user_request;
+                resId = R.string.kg_prompt_after_user_lockdown_pattern;
                 break;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
                 resId = R.string.kg_prompt_reason_timeout_pattern;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 5cb2c5c..687436c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -118,7 +118,7 @@
             case PROMPT_REASON_DEVICE_ADMIN:
                 return R.string.kg_prompt_reason_device_admin;
             case PROMPT_REASON_USER_REQUEST:
-                return R.string.kg_prompt_reason_user_request;
+                return R.string.kg_prompt_after_user_lockdown_pin;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
                 return R.string.kg_prompt_reason_timeout_pin;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index da7bf4e..3f20540 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -266,7 +266,7 @@
     /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
     // TODO(b/286563884): Tracking bug
     @JvmField
-    val KEYGUARD_TALKBACK_FIX = unreleasedFlag(238, "keyguard_talkback_fix")
+    val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix")
 
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 97244c8..5bf56a1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -33,6 +33,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -848,6 +849,8 @@
                 return KeyguardSecurityView.PROMPT_REASON_RESTART;
             } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) {
                 return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+            } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN) != 0) {
+                return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
             } else if ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
                 return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
             } else if (trustAgentsEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index b96ca7a..3b32313e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -224,7 +224,6 @@
                         return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
                     }
                 });
-        controller.overrideIconTintForNavMode(true);
 
         return controller;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 3eec7fa0e..ff57a73 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -39,19 +39,12 @@
      * [cutout]. We don't show battery estimation in qqs header on the devices with center cutout.
      * The result might be null when the battery icon is invisible during the qs-qqs transition
      * animation.
+     *
+     * Note: short-circuiting this value until a comprehensive fix for b/282044659 is finished.
      */
     @BatteryMeterView.BatteryPercentMode
     fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
-        when {
-            qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
-            qsExpandedFraction < fadeOutCompleteFraction ->
-                if (hasCenterCutout(cutout)) {
-                    BatteryMeterView.MODE_ON
-                } else {
-                    BatteryMeterView.MODE_ESTIMATE
-                }
-            else -> null
-        }
+        BatteryMeterView.MODE_ON
 
     fun updateResources() {
         fadeInStartFraction =
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 1cd0f08..648ece5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -475,6 +475,13 @@
      */
     private boolean mShouldDelayWakeUpAnimation = false;
 
+    /**
+     * Whether we should delay the AOD->Lockscreen animation.
+     * If false, the animation will start in onStartedWakingUp().
+     * If true, the animation will start in onFinishedWakingUp().
+     */
+    private boolean mShouldDelayLockscreenTransitionFromAod = false;
+
     private final Object mQueueLock = new Object();
 
     private final PulseExpansionHandler mPulseExpansionHandler;
@@ -3242,7 +3249,10 @@
 
                 updateVisibleToUser();
                 updateIsKeyguard();
-                if (!mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) {
+                mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
+                        && mFeatureFlags.isEnabled(
+                                Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD);
+                if (!mShouldDelayLockscreenTransitionFromAod) {
                     startLockscreenTransitionFromAod();
                 }
             });
@@ -3251,8 +3261,7 @@
 
         /**
          * Private helper for starting the LOCKSCREEN_TRANSITION_FROM_AOD animation - only necessary
-         * so we can start it from either onFinishedWakingUp() or onFinishedWakingUp() depending
-         * on a flag value.
+         * so we can start it from either onFinishedWakingUp() or onFinishedWakingUp().
          */
         private void startLockscreenTransitionFromAod() {
             // stopDozing() starts the LOCKSCREEN_TRANSITION_FROM_AOD animation.
@@ -3273,7 +3282,7 @@
 
         @Override
         public void onFinishedWakingUp() {
-            if (mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) {
+            if (mShouldDelayLockscreenTransitionFromAod) {
                 mNotificationShadeWindowController.batchApplyWindowLayoutParams(
                         this::startLockscreenTransitionFromAod);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 7bc4fc3..ae70384 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -115,7 +115,6 @@
     };
 
     private final Context mContext;
-    private Boolean mOverrideIconTintForNavMode;
 
     @AssistedInject
     public LightBarTransitionsController(
@@ -276,19 +275,11 @@
     }
 
     /**
-     * Specify an override value to return for {@link #overrideIconTintForNavMode(boolean)}.
-     */
-    public void overrideIconTintForNavMode(boolean overrideValue) {
-        mOverrideIconTintForNavMode = overrideValue;
-    }
-    /**
      * Return whether to use the tint calculated in this class for nav icons.
      */
     public boolean supportsIconTintForNavMode(int navigationMode) {
         // In gesture mode, we already do region sampling to update tint based on content beneath.
-        return mOverrideIconTintForNavMode != null
-                ? mOverrideIconTintForNavMode
-                : !QuickStepContract.isGesturalMode(navigationMode);
+        return !QuickStepContract.isGesturalMode(navigationMode);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1f353e1..8a422c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -21,6 +21,7 @@
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -356,6 +357,42 @@
     }
 
     @Test
+    public void testBouncerPrompt_afterUserLockDown() {
+        // GIVEN biometrics enrolled
+        when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(true);
+
+        // WHEN user has locked down the device
+        KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
+                mock(KeyguardUpdateMonitor.StrongAuthTracker.class);
+        when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(strongAuthTracker);
+        when(strongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
+        when(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        // THEN the bouncer prompt reason should return PROMPT_REASON_USER_REQUEST
+        assertEquals(KeyguardSecurityView.PROMPT_REASON_USER_REQUEST,
+                mViewMediator.mViewMediatorCallback.getBouncerPromptReason());
+    }
+
+    @Test
+    public void testBouncerPrompt_afterUserLockDown_noBiometricsEnrolled() {
+        // GIVEN biometrics not enrolled
+        when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(false);
+
+        // WHEN user has locked down the device
+        KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
+                mock(KeyguardUpdateMonitor.StrongAuthTracker.class);
+        when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(strongAuthTracker);
+        when(strongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
+        when(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        // THEN the bouncer prompt reason should return the default prompt
+        assertEquals(KeyguardSecurityView.PROMPT_REASON_NONE,
+                mViewMediator.mViewMediatorCallback.getBouncerPromptReason());
+    }
+
+    @Test
     public void testBouncerPrompt_nonStrongIdleTimeout() {
         // GIVEN trust agents enabled and biometrics are enrolled
         when(mUpdateMonitor.isTrustUsuallyManaged(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index d421aca..b028f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -63,36 +63,40 @@
 
     @Test
     fun returnsMODE_ESTIMATEforQsWithCenterCutout() {
+        // TODO (b/282044659): revert this test to previous behavior
         assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
-            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+            .isEqualTo(BatteryMeterView.MODE_ON)
     }
 
     @Test
     fun returnsMODE_ONforQqsWithCornerCutout() {
         whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
 
+        // TODO (b/282044659): revert this test to previous behavior
         assertThat(
                 controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.prevFrameToFraction())
             )
-            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+            .isEqualTo(BatteryMeterView.MODE_ON)
     }
 
     @Test
     fun returnsMODE_ESTIMATEforQsWithCornerCutout() {
         whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
 
+        // TODO (b/282044659): revert this test to previous behavior
         assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
-            .isEqualTo(BatteryMeterView.MODE_ESTIMATE)
+            .isEqualTo(BatteryMeterView.MODE_ON)
     }
 
     @Test
     fun returnsNullInBetween() {
+        // TODO (b/282044659): revert this test to previous behavior
         assertThat(
                 controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.nextFrameToFraction())
             )
-            .isNull()
+            .isEqualTo(BatteryMeterView.MODE_ON)
         assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.prevFrameToFraction()))
-            .isNull()
+            .isEqualTo(BatteryMeterView.MODE_ON)
     }
 
     private fun Int.prevFrameToFraction(): Float = (this - 1) / MOTION_LAYOUT_MAX_FRAME.toFloat()
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 8062272..7702630 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
@@ -350,7 +350,9 @@
         // For the Shade to animate during the Back gesture, we must enable the animation flag.
         mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
         mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
+        // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
+        when(mDozeParameters.getAlwaysOn()).thenReturn(true);
 
         IThermalService thermalService = mock(IThermalService.class);
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 33e21f1..03d1fbb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1608,11 +1608,15 @@
                     refreshProcessQueuesLocked(uid);
                 }
             }
+        }, ActivityManager.UID_OBSERVER_PROCSTATE,
+                ActivityManager.PROCESS_STATE_TOP, "android");
 
+        mService.registerUidObserver(new UidObserver() {
             @Override
-            public void onUidCachedChanged(int uid, boolean cached) {
+            public void onUidStateChanged(int uid, int procState, long procStateSeq,
+                    int capability) {
                 synchronized (mService) {
-                    if (cached) {
+                    if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                         mUidCached.put(uid, true);
                     } else {
                         mUidCached.delete(uid);
@@ -1620,8 +1624,8 @@
                     refreshProcessQueuesLocked(uid);
                 }
             }
-        }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED,
-                ActivityManager.PROCESS_STATE_TOP, "android");
+        }, ActivityManager.UID_OBSERVER_PROCSTATE,
+                ActivityManager.PROCESS_STATE_LAST_ACTIVITY, "android");
 
         // Kick off periodic health checks
         mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 7773190..eccff2a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -123,7 +123,14 @@
             "freeze_debounce_timeout";
     @VisibleForTesting static final String KEY_FREEZER_EXEMPT_INST_PKG =
             "freeze_exempt_inst_pkg";
-
+    @VisibleForTesting static final String KEY_FREEZER_BINDER_ENABLED =
+            "freeze_binder_enabled";
+    @VisibleForTesting static final String KEY_FREEZER_BINDER_DIVISOR =
+            "freeze_binder_divisor";
+    @VisibleForTesting static final String KEY_FREEZER_BINDER_OFFSET =
+            "freeze_binder_offset";
+    @VisibleForTesting static final String KEY_FREEZER_BINDER_THRESHOLD =
+            "freeze_binder_threshold";
 
     static final int UNFREEZE_REASON_NONE =
             FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_NONE;
@@ -237,8 +244,8 @@
     @VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
 
     // Defaults for phenotype flags.
-    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true;
-    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
+    @VisibleForTesting static final boolean DEFAULT_USE_COMPACTION = true;
+    @VisibleForTesting static final boolean DEFAULT_USE_FREEZER = true;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -257,7 +264,11 @@
     @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
             String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
     @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L;
-    @VisibleForTesting static final Boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
+    @VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
+    @VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_ENABLED = true;
+    @VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4;
+    @VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500;
+    @VisibleForTesting static final long DEFAULT_FREEZER_BINDER_THRESHOLD = 1_000;
 
     @VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
                 Settings.Global.CACHED_APPS_FREEZER_ENABLED);
@@ -393,6 +404,11 @@
                                 updateFreezerDebounceTimeout();
                             } else if (KEY_FREEZER_EXEMPT_INST_PKG.equals(name)) {
                                 updateFreezerExemptInstPkg();
+                            } else if (KEY_FREEZER_BINDER_ENABLED.equals(name)
+                                    || KEY_FREEZER_BINDER_DIVISOR.equals(name)
+                                    || KEY_FREEZER_BINDER_THRESHOLD.equals(name)
+                                    || KEY_FREEZER_BINDER_OFFSET.equals(name)) {
+                                updateFreezerBinderState();
                             }
                         }
                     }
@@ -455,6 +471,16 @@
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting final Set<Integer> mProcStateThrottle;
 
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting volatile boolean mFreezerBinderEnabled = DEFAULT_FREEZER_BINDER_ENABLED;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting volatile long mFreezerBinderDivisor = DEFAULT_FREEZER_BINDER_DIVISOR;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting volatile int mFreezerBinderOffset = DEFAULT_FREEZER_BINDER_OFFSET;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting volatile long mFreezerBinderThreshold = DEFAULT_FREEZER_BINDER_THRESHOLD;
+
+
     // Handler on which compaction runs.
     @VisibleForTesting
     Handler mCompactionHandler;
@@ -759,6 +785,10 @@
             pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
             pw.println("  " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
             pw.println("  " + KEY_FREEZER_EXEMPT_INST_PKG + "=" + mFreezerExemptInstPkg);
+            pw.println("  " + KEY_FREEZER_BINDER_ENABLED + "=" + mFreezerBinderEnabled);
+            pw.println("  " + KEY_FREEZER_BINDER_THRESHOLD + "=" + mFreezerBinderThreshold);
+            pw.println("  " + KEY_FREEZER_BINDER_DIVISOR + "=" + mFreezerBinderDivisor);
+            pw.println("  " + KEY_FREEZER_BINDER_OFFSET + "=" + mFreezerBinderOffset);
             synchronized (mProcLock) {
                 int size = mFrozenProcesses.size();
                 pw.println("  Apps frozen: " + size);
@@ -1264,6 +1294,26 @@
         Slog.d(TAG_AM, "Freezer exemption set to " + mFreezerExemptInstPkg);
     }
 
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateFreezerBinderState() {
+        mFreezerBinderEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                KEY_FREEZER_BINDER_ENABLED, DEFAULT_FREEZER_BINDER_ENABLED);
+        mFreezerBinderDivisor = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                KEY_FREEZER_BINDER_DIVISOR, DEFAULT_FREEZER_BINDER_DIVISOR);
+        mFreezerBinderOffset = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                KEY_FREEZER_BINDER_OFFSET, DEFAULT_FREEZER_BINDER_OFFSET);
+        mFreezerBinderThreshold = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                KEY_FREEZER_BINDER_THRESHOLD, DEFAULT_FREEZER_BINDER_THRESHOLD);
+        Slog.d(TAG_AM, "Freezer binder state set to enabled=" + mFreezerBinderEnabled
+                + ", divisor=" + mFreezerBinderDivisor
+                + ", offset=" + mFreezerBinderOffset
+                + ", threshold=" + mFreezerBinderThreshold);
+    }
+
     private boolean parseProcStateThrottle(String procStateThrottleString) {
         String[] procStates = TextUtils.split(procStateThrottleString, ",");
         mProcStateThrottle.clear();
@@ -1352,6 +1402,7 @@
                 }
             }
         }
+        app.mOptRecord.setLastUsedTimeout(delayMillis);
         mFreezeHandler.sendMessageDelayed(
                 mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
                 delayMillis);
@@ -1525,10 +1576,14 @@
                 opt.setPendingFreeze(false);
             }
 
-            UidRecord uidRec = app.getUidRecord();
-            if (uidRec != null && uidRec.isFrozen()) {
-                uidRec.setFrozen(false);
-                postUidFrozenMessage(uidRec.getUid(), false);
+            final UidRecord uidRec = app.getUidRecord();
+            if (uidRec != null) {
+                final boolean isFrozen = uidRec.getNumOfProcs() > 1
+                        && uidRec.areAllProcessesFrozen(app);
+                if (isFrozen != uidRec.isFrozen()) {
+                    uidRec.setFrozen(isFrozen);
+                    postUidFrozenMessage(uidRec.getUid(), isFrozen);
+                }
             }
 
             mFrozenProcesses.delete(app.getPid());
@@ -2117,12 +2172,54 @@
         }
 
         @GuardedBy({"mAm", "mProcLock"})
-        private void rescheduleFreeze(final ProcessRecord proc, final String reason,
-                @UnfreezeReason int reasonCode) {
+        private void handleBinderFreezerFailure(final ProcessRecord proc, final String reason) {
+            if (!mFreezerBinderEnabled) {
+                // Just reschedule indefinitely.
+                unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS);
+                freezeAppAsyncLSP(proc);
+                return;
+            }
+            /*
+             * This handles the case where a process couldn't be frozen due to pending binder
+             * transactions. In order to prevent apps from avoiding the freezer by spamming binder
+             * transactions, there is an exponential decrease in freezer retry times plus a random
+             * offset per attempt to avoid phase issues. Once the last-attempted timeout is below a
+             * threshold, we assume that the app is spamming binder calls and can never be frozen,
+             * and we will then crash the app.
+             */
+            if (proc.mOptRecord.getLastUsedTimeout() <= mFreezerBinderThreshold) {
+                // We've given the app plenty of chances, assume broken. Time to die.
+                Slog.d(TAG_AM, "Kill app due to repeated failure to freeze binder: "
+                        + proc.getPid() + " " + proc.processName);
+                mAm.mHandler.post(() -> {
+                    synchronized (mAm) {
+                        // Crash regardless of procstate in case the app has found another way
+                        // to abuse oom_adj
+                        if (proc.getThread() == null) {
+                            return;
+                        }
+                        proc.killLocked("excessive binder traffic during cached",
+                                ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
+                                ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
+                                true);
+                    }
+                });
+                return;
+            }
+
+            long timeout = proc.mOptRecord.getLastUsedTimeout() / mFreezerBinderDivisor;
+            // range is [-mFreezerBinderOffset, +mFreezerBinderOffset]
+            int offset = mRandom.nextInt(mFreezerBinderOffset * 2) - mFreezerBinderOffset;
+            timeout = Math.max(timeout + offset, mFreezerBinderThreshold);
+
             Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid()
-                    + " " + proc.processName + " (" + reason + ")");
-            unfreezeAppLSP(proc, reasonCode);
-            freezeAppAsyncLSP(proc);
+                    + " " + proc.processName + " (" + reason  + "), timeout=" + timeout);
+            Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_FREEZER_TRACK,
+                    "Reschedule freeze " + proc.processName + ":" + proc.getPid()
+                    + " timeout=" + timeout + ", reason=" + reason);
+
+            unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS);
+            freezeAppAsyncLSP(proc, timeout);
         }
 
         /**
@@ -2169,7 +2266,7 @@
                 // transactions that might be pending.
                 try {
                     if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
-                        rescheduleFreeze(proc, "outstanding txns", UNFREEZE_REASON_BINDER_TXNS);
+                        handleBinderFreezerFailure(proc, "outstanding txns");
                         return;
                     }
                 } catch (RuntimeException e) {
@@ -2230,7 +2327,7 @@
 
                 if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
                     synchronized (mProcLock) {
-                        rescheduleFreeze(proc, "new pending txns", UNFREEZE_REASON_BINDER_TXNS);
+                        handleBinderFreezerFailure(proc, "new pending txns");
                     }
                     return;
                 }
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index ffe5a6e..f5c5ea8 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -127,6 +127,12 @@
     @GuardedBy("mProcLock")
     private @UptimeMillisLong long mEarliestFreezableTimeMillis;
 
+    /**
+     * This is the most recently used timeout for freezing the app in millis
+     */
+    @GuardedBy("mProcLock")
+    private long mLastUsedTimeout;
+
     @GuardedBy("mProcLock")
     long getLastCompactTime() {
         return mLastCompactTime;
@@ -282,6 +288,16 @@
     }
 
     @GuardedBy("mProcLock")
+    long getLastUsedTimeout() {
+        return mLastUsedTimeout;
+    }
+
+    @GuardedBy("mProcLock")
+    void setLastUsedTimeout(long lastUsedTimeout) {
+        mLastUsedTimeout = lastUsedTimeout;
+    }
+
+    @GuardedBy("mProcLock")
     boolean isFreezeExempt() {
         return mFreezeExempt;
     }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 993088e..4329afc 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -345,21 +345,32 @@
     }
 
     /**
+     * Check whether all processes in the Uid are frozen.
+     *
+     * @param excluding Skip this process record during the check.
      * @return true if all processes in the Uid are frozen, false otherwise.
      */
     @GuardedBy(anyOf = {"mService", "mProcLock"})
-    public boolean areAllProcessesFrozen() {
+    public boolean areAllProcessesFrozen(ProcessRecord excluding) {
         for (int i = mProcRecords.size() - 1; i >= 0; i--) {
             final ProcessRecord app = mProcRecords.valueAt(i);
             final ProcessCachedOptimizerRecord opt = app.mOptRecord;
 
-            if (!opt.isFrozen()) {
+            if (excluding != app && !opt.isFrozen()) {
                 return false;
             }
         }
         return true;
     }
 
+    /**
+     * @return true if all processes in the Uid are frozen, false otherwise.
+     */
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    public boolean areAllProcessesFrozen() {
+        return areAllProcessesFrozen(null);
+    }
+
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     public void setFrozen(boolean frozen) {
         mUidIsFrozen = frozen;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index ec85d57..0c7f11f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1199,11 +1199,16 @@
                     AudioDeviceInfo device = Stream.of(connectedDevices)
                             .filter(d -> d.getInternalType() == ada.getInternalType())
                             .filter(d -> (!AudioSystem.isBluetoothDevice(d.getInternalType())
-                                            || (d.getAddress() == ada.getAddress())))
+                                            || (d.getAddress().equals(ada.getAddress()))))
                             .findFirst()
                             .orElse(null);
 
                     if (device == null) {
+                        if (AudioService.DEBUG_DEVICES) {
+                            Slog.i(TAG, "purgeRoles() removing device: " + ada.toString()
+                                    + ", for strategy: " + keyRole.first
+                                    + " and role: " + keyRole.second);
+                        }
                         asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada));
                         itDev.remove();
                     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d29d9c8..488745c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8718,13 +8718,7 @@
 
         ToastRecord lastToast = mToastQueue.remove(index);
 
-        mWindowManagerInternal.removeWindowToken(lastToast.windowToken, false /* removeWindows */,
-                lastToast.displayId);
-        // We passed 'false' for 'removeWindows' so that the client has time to stop
-        // rendering (as hide above is a one-way message), otherwise we could crash
-        // a client which was actively using a surface made from the token. However
-        // we need to schedule a timeout to make sure the token is eventually killed
-        // one way or another.
+        // We need to schedule a timeout to make sure the token is eventually killed
         scheduleKillTokenTimeout(lastToast);
 
         keepProcessAliveForToastIfNeededLocked(record.pid);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index abfc1d7..78f1fa6 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4311,7 +4311,12 @@
         if (Process.isIsolatedUid(uid)
                 && mPermissionManager.getHotwordDetectionServiceProvider() != null
                 && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
-            uid = getIsolatedOwner(uid);
+            try {
+                uid = getIsolatedOwner(uid);
+            } catch (IllegalStateException e) {
+                // If the owner uid doesn't exist, just use the current uid
+                Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e);
+            }
         }
         final int callingUserId = UserHandle.getUserId(callingUid);
         final int appId = UserHandle.getAppId(uid);
@@ -4352,7 +4357,12 @@
             if (Process.isIsolatedUid(uid)
                     && mPermissionManager.getHotwordDetectionServiceProvider() != null
                     && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
-                uid = getIsolatedOwner(uid);
+                try {
+                    uid = getIsolatedOwner(uid);
+                } catch (IllegalStateException e) {
+                    // If the owner uid doesn't exist, just use the current uid
+                    Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e);
+                }
             }
             final int appId = UserHandle.getAppId(uid);
             final Object obj = mSettings.getSettingBase(appId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index e7a94c0..7a88069 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -172,6 +172,7 @@
     BroadcastConstants mConstants;
     private BroadcastSkipPolicy mSkipPolicy;
     private UidObserver mUidObserver;
+    private UidObserver mUidCachedStateObserver;
 
     /**
      * Desired behavior of the next
@@ -317,7 +318,13 @@
         doAnswer((invocation) -> {
             mUidObserver = invocation.getArgument(0);
             return null;
-        }).when(mAms).registerUidObserver(any(), anyInt(), anyInt(), any());
+        }).when(mAms).registerUidObserver(any(), anyInt(),
+                eq(ActivityManager.PROCESS_STATE_TOP), any());
+        doAnswer((invocation) -> {
+            mUidCachedStateObserver = invocation.getArgument(0);
+            return null;
+        }).when(mAms).registerUidObserver(any(), anyInt(),
+                eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
 
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.TIMEOUT = 100;
@@ -1762,8 +1769,12 @@
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
         final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
 
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_GREEN),
+                ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_BLUE),
+                ActivityManager.PROCESS_STATE_CACHED_EMPTY, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
 
         final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1807,12 +1818,16 @@
                 eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
 
         // Shift blue to be active and confirm that deferred broadcast is delivered
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_BLUE),
+                ActivityManager.PROCESS_STATE_TOP, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_GREEN),
+                ActivityManager.PROCESS_STATE_SERVICE, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
     }
@@ -2237,9 +2252,15 @@
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
 
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_YELLOW), false);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_GREEN),
+                ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_BLUE),
+                ActivityManager.PROCESS_STATE_CACHED_EMPTY, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_YELLOW),
+                ActivityManager.PROCESS_STATE_SERVICE, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2262,7 +2283,9 @@
         verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+        mUidCachedStateObserver.onUidStateChanged(getUidForPackage(PACKAGE_GREEN),
+                ActivityManager.PROCESS_STATE_TOP_SLEEPING, 0,
+                ActivityManager.PROCESS_CAPABILITY_NONE);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
     }