Merge "Fix permission check for unbundled sharesheet" into tm-qpr-dev
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a389223..a2fa139 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -58,13 +58,11 @@
             setTitle(title);
         }
 
-        final Object callback = getIntent().getExtras().getBinder(EXTRA_CALLBACK);
-        if (callback instanceof DreamService.DreamActivityCallbacks) {
-            mCallback = (DreamService.DreamActivityCallbacks) callback;
+        final Bundle extras = getIntent().getExtras();
+        mCallback = (DreamService.DreamActivityCallbacks) extras.getBinder(EXTRA_CALLBACK);
+
+        if (mCallback != null) {
             mCallback.onActivityCreated(this);
-        } else {
-            mCallback = null;
-            finishAndRemoveTask();
         }
     }
 
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index d79ea89..2243072 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1409,6 +1409,10 @@
                             // Request the DreamOverlay be told to dream with dream's window
                             // parameters once the window has been attached.
                             mDreamStartOverlayConsumer = overlay -> {
+                                if (mWindow == null) {
+                                    Slog.d(TAG, "mWindow is null");
+                                    return;
+                                }
                                 try {
                                     overlay.startDream(mWindow.getAttributes(), mOverlayCallback,
                                             mDreamComponent.flattenToString(),
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 72b9cd2..818a503 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -73,6 +73,7 @@
 
 import java.io.PrintWriter;
 import java.util.Comparator;
+import java.util.concurrent.TimeUnit;
 
 public final class ProcessState {
     private static final String TAG = "ProcessStats";
@@ -1542,6 +1543,75 @@
         proto.write(fieldId, procName);
     }
 
+    /** Dumps the duration of each state to statsEventOutput. */
+    public void dumpStateDurationToStatsd(
+            int atomTag, ProcessStats processStats, StatsEventOutput statsEventOutput) {
+        long topMs = 0;
+        long fgsMs = 0;
+        long boundTopMs = 0;
+        long boundFgsMs = 0;
+        long importantForegroundMs = 0;
+        long cachedMs = 0;
+        long frozenMs = 0;
+        long otherMs = 0;
+        for (int i = 0, size = mDurations.getKeyCount(); i < size; i++) {
+            final int key = mDurations.getKeyAt(i);
+            final int type = SparseMappingTable.getIdFromKey(key);
+            int procStateIndex = type % STATE_COUNT;
+            long duration = mDurations.getValue(key);
+            switch (procStateIndex) {
+                case STATE_TOP:
+                    topMs += duration;
+                    break;
+                case STATE_BOUND_TOP_OR_FGS:
+                    boundTopMs += duration;
+                    break;
+                case STATE_FGS:
+                    fgsMs += duration;
+                    break;
+                case STATE_IMPORTANT_FOREGROUND:
+                case STATE_IMPORTANT_BACKGROUND:
+                    importantForegroundMs += duration;
+                    break;
+                case STATE_BACKUP:
+                case STATE_SERVICE:
+                case STATE_SERVICE_RESTARTING:
+                case STATE_RECEIVER:
+                case STATE_HEAVY_WEIGHT:
+                case STATE_HOME:
+                case STATE_LAST_ACTIVITY:
+                case STATE_PERSISTENT:
+                    otherMs += duration;
+                    break;
+                case STATE_CACHED_ACTIVITY:
+                case STATE_CACHED_ACTIVITY_CLIENT:
+                case STATE_CACHED_EMPTY:
+                    cachedMs += duration;
+                    break;
+                    // TODO (b/261910877) Add support for tracking boundFgsMs and
+                    // frozenMs.
+            }
+        }
+        statsEventOutput.write(
+                atomTag,
+                getUid(),
+                getName(),
+                (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodStartUptime),
+                (int) TimeUnit.MILLISECONDS.toSeconds(processStats.mTimePeriodEndUptime),
+                (int)
+                        TimeUnit.MILLISECONDS.toSeconds(
+                                processStats.mTimePeriodEndUptime
+                                        - processStats.mTimePeriodStartUptime),
+                (int) TimeUnit.MILLISECONDS.toSeconds(topMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(fgsMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(boundTopMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(boundFgsMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(importantForegroundMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(cachedMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(frozenMs),
+                (int) TimeUnit.MILLISECONDS.toSeconds(otherMs));
+    }
+
     /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
     public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
             String procName, int uid, long now,
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index d2b2f0a..f3ed09a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -43,6 +43,7 @@
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.procstats.AssociationState.SourceKey;
 import com.android.internal.app.procstats.AssociationState.SourceState;
+import com.android.internal.util.function.QuintConsumer;
 
 import dalvik.system.VMRuntime;
 
@@ -56,6 +57,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -2389,6 +2392,79 @@
         }
     }
 
+    void forEachProcess(Consumer<ProcessState> consumer) {
+        final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int ip = 0, size = procMap.size(); ip < size; ip++) {
+            final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+            for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+                final ProcessState processState = uids.valueAt(iu);
+                consumer.accept(processState);
+            }
+        }
+    }
+
+    void forEachAssociation(
+            QuintConsumer<AssociationState, Integer, String, SourceKey, SourceState> consumer) {
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
+        for (int ip = 0, size = pkgMap.size(); ip < size; ip++) {
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            for (int iu = 0, uidsSize = uids.size(); iu < uidsSize; iu++) {
+                final int uid = uids.keyAt(iu);
+                final LongSparseArray<PackageState> versions = uids.valueAt(iu);
+                for (int iv = 0, versionsSize = versions.size(); iv < versionsSize; iv++) {
+                    final PackageState state = versions.valueAt(iv);
+                    for (int iasc = 0, ascSize = state.mAssociations.size();
+                            iasc < ascSize;
+                            iasc++) {
+                        final String serviceName = state.mAssociations.keyAt(iasc);
+                        final AssociationState asc = state.mAssociations.valueAt(iasc);
+                        for (int is = 0, sourcesSize = asc.mSources.size();
+                                is < sourcesSize;
+                                is++) {
+                            final SourceState src = asc.mSources.valueAt(is);
+                            final SourceKey key = asc.mSources.keyAt(is);
+                            consumer.accept(asc, uid, serviceName, key, src);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /** Dumps the stats of all processes to statsEventOutput. */
+    public void dumpProcessState(int atomTag, StatsEventOutput statsEventOutput) {
+        forEachProcess(
+                (processState) -> {
+                    if (processState.isMultiPackage()
+                            && processState.getCommonProcess() != processState) {
+                        return;
+                    }
+                    processState.dumpStateDurationToStatsd(atomTag, this, statsEventOutput);
+                });
+    }
+
+    /** Dumps all process association data to statsEventOutput. */
+    public void dumpProcessAssociation(int atomTag, StatsEventOutput statsEventOutput) {
+        forEachAssociation(
+                (asc, serviceUid, serviceName, key, src) -> {
+                    statsEventOutput.write(
+                            atomTag,
+                            key.mUid,
+                            key.mProcess,
+                            serviceUid,
+                            serviceName,
+                            (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodStartUptime),
+                            (int) TimeUnit.MILLISECONDS.toSeconds(mTimePeriodEndUptime),
+                            (int)
+                                    TimeUnit.MILLISECONDS.toSeconds(
+                                            mTimePeriodEndUptime - mTimePeriodStartUptime),
+                            (int) TimeUnit.MILLISECONDS.toSeconds(src.mDuration),
+                            src.mActiveCount,
+                            asc.getProcessName());
+                });
+    }
+
     private void dumpProtoPreamble(ProtoOutputStream proto) {
         proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
         proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
diff --git a/core/java/com/android/internal/app/procstats/StatsEventOutput.java b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
new file mode 100644
index 0000000..b2e4054
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/StatsEventOutput.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.List;
+
+/**
+ * A simple wrapper of FrameworkStatsLog.buildStatsEvent. This allows unit tests to mock out the
+ * dependency.
+ */
+public class StatsEventOutput {
+
+    List<StatsEvent> mOutput;
+
+    public StatsEventOutput(List<StatsEvent> output) {
+        mOutput = output;
+    }
+
+    /** Writes the data to the output. */
+    public void write(
+            int atomTag,
+            int uid,
+            String processName,
+            int measurementStartUptimeSecs,
+            int measurementEndUptimeSecs,
+            int measurementDurationUptimeSecs,
+            int topSeconds,
+            int fgsSeconds,
+            int boundTopSeconds,
+            int boundFgsSeconds,
+            int importantForegroundSeconds,
+            int cachedSeconds,
+            int frozenSeconds,
+            int otherSeconds) {
+        mOutput.add(
+                FrameworkStatsLog.buildStatsEvent(
+                        atomTag,
+                        uid,
+                        processName,
+                        measurementStartUptimeSecs,
+                        measurementEndUptimeSecs,
+                        measurementDurationUptimeSecs,
+                        topSeconds,
+                        fgsSeconds,
+                        boundTopSeconds,
+                        boundFgsSeconds,
+                        importantForegroundSeconds,
+                        cachedSeconds,
+                        frozenSeconds,
+                        otherSeconds));
+    }
+
+    /** Writes the data to the output. */
+    public void write(
+            int atomTag,
+            int clientUid,
+            String processName,
+            int serviceUid,
+            String serviceName,
+            int measurementStartUptimeSecs,
+            int measurementEndUptimeSecs,
+            int measurementDurationUptimeSecs,
+            int activeDurationUptimeSecs,
+            int activeCount,
+            String serviceProcessName) {
+        mOutput.add(
+                FrameworkStatsLog.buildStatsEvent(
+                        atomTag,
+                        clientUid,
+                        processName,
+                        serviceUid,
+                        serviceName,
+                        measurementStartUptimeSecs,
+                        measurementEndUptimeSecs,
+                        measurementDurationUptimeSecs,
+                        activeDurationUptimeSecs,
+                        activeCount,
+                        serviceProcessName));
+    }
+}
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/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
new file mode 100644
index 0000000..9b9a84b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app.procstats;
+
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.mockito.Mock;
+
+import java.util.concurrent.TimeUnit;
+
+/** Provides test cases for ProcessStats. */
+public class ProcessStatsTest extends TestCase {
+
+    private static final String APP_1_PACKAGE_NAME = "com.android.testapp";
+    private static final int APP_1_UID = 5001;
+    private static final long APP_1_VERSION = 10;
+    private static final String APP_1_PROCESS_NAME = "com.android.testapp.p";
+    private static final String APP_1_SERVICE_NAME = "com.android.testapp.service";
+
+    private static final String APP_2_PACKAGE_NAME = "com.android.testapp2";
+    private static final int APP_2_UID = 5002;
+    private static final long APP_2_VERSION = 30;
+    private static final String APP_2_PROCESS_NAME = "com.android.testapp2.p";
+
+    private static final long NOW_MS = 123000;
+    private static final int DURATION_SECS = 6;
+
+    @Mock StatsEventOutput mStatsEventOutput;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+    }
+
+    @SmallTest
+    public void testDumpProcessState() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        processStats.getProcessStateLocked(
+                APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processStats.getProcessStateLocked(
+                APP_2_PACKAGE_NAME, APP_2_UID, APP_2_VERSION, APP_2_PROCESS_NAME);
+        processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_1_UID),
+                        eq(APP_1_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_2_UID),
+                        eq(APP_2_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+    }
+
+    @SmallTest
+    public void testNonZeroProcessStateDuration() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        ProcessState processState =
+                processStats.getProcessStateLocked(
+                        APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processState.setCombinedState(STATE_TOP, NOW_MS);
+        processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS));
+        processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_STATE),
+                        eq(APP_1_UID),
+                        eq(APP_1_PROCESS_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(DURATION_SECS),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(0));
+    }
+
+    @SmallTest
+    public void testDumpProcessAssociation() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        AssociationState associationState =
+                processStats.getAssociationStateLocked(
+                        APP_1_PACKAGE_NAME,
+                        APP_1_UID,
+                        APP_1_VERSION,
+                        APP_1_PROCESS_NAME,
+                        APP_1_SERVICE_NAME);
+        AssociationState.SourceState sourceState =
+                associationState.startSource(APP_2_UID, APP_2_PROCESS_NAME, APP_2_PACKAGE_NAME);
+        sourceState.stop();
+        processStats.dumpProcessAssociation(
+                FrameworkStatsLog.PROCESS_ASSOCIATION, mStatsEventOutput);
+        verify(mStatsEventOutput)
+                .write(
+                        eq(FrameworkStatsLog.PROCESS_ASSOCIATION),
+                        eq(APP_2_UID),
+                        eq(APP_2_PROCESS_NAME),
+                        eq(APP_1_UID),
+                        eq(APP_1_SERVICE_NAME),
+                        anyInt(),
+                        anyInt(),
+                        eq(0),
+                        eq(0),
+                        eq(0),
+                        eq(APP_1_PROCESS_NAME));
+    }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 1cf819a..3d7fb16b 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1051,6 +1051,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
+    "-1104347731": {
+      "message": "Setting requested orientation %s for %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1103716954": {
       "message": "Not removing %s due to exit animation",
       "level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index c08085e..541c0f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -135,6 +135,30 @@
     private static final boolean BUBBLE_BAR_ENABLED =
             SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
 
+
+    /**
+     * Common interface to send updates to bubble views.
+     */
+    public interface BubbleViewCallback {
+        /** Called when the provided bubble should be removed. */
+        void removeBubble(Bubble removedBubble);
+        /** Called when the provided bubble should be added. */
+        void addBubble(Bubble addedBubble);
+        /** Called when the provided bubble should be updated. */
+        void updateBubble(Bubble updatedBubble);
+        /** Called when the provided bubble should be selected. */
+        void selectionChanged(BubbleViewProvider selectedBubble);
+        /** Called when the provided bubble's suppression state has changed. */
+        void suppressionChanged(Bubble bubble, boolean isSuppressed);
+        /** Called when the expansion state of bubbles has changed. */
+        void expansionChanged(boolean isExpanded);
+        /**
+         * Called when the order of the bubble list has changed. Depending on the expanded state
+         * the pointer might need to be updated.
+         */
+        void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer);
+    }
+
     private final Context mContext;
     private final BubblesImpl mImpl = new BubblesImpl();
     private Bubbles.BubbleExpandListener mExpandListener;
@@ -157,7 +181,6 @@
     // Used to post to main UI thread
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
-
     private final ShellExecutor mBackgroundExecutor;
 
     private BubbleLogger mLogger;
@@ -1285,6 +1308,58 @@
         });
     }
 
+    private final BubbleViewCallback mBubbleViewCallback = new BubbleViewCallback() {
+        @Override
+        public void removeBubble(Bubble removedBubble) {
+            if (mStackView != null) {
+                mStackView.removeBubble(removedBubble);
+            }
+        }
+
+        @Override
+        public void addBubble(Bubble addedBubble) {
+            if (mStackView != null) {
+                mStackView.addBubble(addedBubble);
+            }
+        }
+
+        @Override
+        public void updateBubble(Bubble updatedBubble) {
+            if (mStackView != null) {
+                mStackView.updateBubble(updatedBubble);
+            }
+        }
+
+        @Override
+        public void bubbleOrderChanged(List<Bubble> bubbleOrder, boolean updatePointer) {
+            if (mStackView != null) {
+                mStackView.updateBubbleOrder(bubbleOrder, updatePointer);
+            }
+        }
+
+        @Override
+        public void suppressionChanged(Bubble bubble, boolean isSuppressed) {
+            if (mStackView != null) {
+                mStackView.setBubbleSuppressed(bubble, isSuppressed);
+            }
+        }
+
+        @Override
+        public void expansionChanged(boolean isExpanded) {
+            if (mStackView != null) {
+                mStackView.setExpanded(isExpanded);
+            }
+        }
+
+        @Override
+        public void selectionChanged(BubbleViewProvider selectedBubble) {
+            if (mStackView != null) {
+                mStackView.setSelectedBubble(selectedBubble);
+            }
+
+        }
+    };
+
     @SuppressWarnings("FieldCanBeLocal")
     private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
 
@@ -1307,7 +1382,8 @@
             // Lazy load overflow bubbles from disk
             loadOverflowBubblesFromDisk();
 
-            mStackView.updateOverflowButtonDot();
+            // If bubbles in the overflow have a dot, make sure the overflow shows a dot
+            updateOverflowButtonDot();
 
             // Update bubbles in overflow.
             if (mOverflowListener != null) {
@@ -1322,9 +1398,7 @@
                 final Bubble bubble = removed.first;
                 @Bubbles.DismissReason final int reason = removed.second;
 
-                if (mStackView != null) {
-                    mStackView.removeBubble(bubble);
-                }
+                mBubbleViewCallback.removeBubble(bubble);
 
                 // Leave the notification in place if we're dismissing due to user switching, or
                 // because DND is suppressing the bubble. In both of those cases, we need to be able
@@ -1354,49 +1428,47 @@
             }
             mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
 
-            if (update.addedBubble != null && mStackView != null) {
+            if (update.addedBubble != null) {
                 mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
-                mStackView.addBubble(update.addedBubble);
+                mBubbleViewCallback.addBubble(update.addedBubble);
             }
 
-            if (update.updatedBubble != null && mStackView != null) {
-                mStackView.updateBubble(update.updatedBubble);
+            if (update.updatedBubble != null) {
+                mBubbleViewCallback.updateBubble(update.updatedBubble);
             }
 
-            if (update.suppressedBubble != null && mStackView != null) {
-                mStackView.setBubbleSuppressed(update.suppressedBubble, true);
+            if (update.suppressedBubble != null) {
+                mBubbleViewCallback.suppressionChanged(update.suppressedBubble, true);
             }
 
-            if (update.unsuppressedBubble != null && mStackView != null) {
-                mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
+            if (update.unsuppressedBubble != null) {
+                mBubbleViewCallback.suppressionChanged(update.unsuppressedBubble, false);
             }
 
             boolean collapseStack = update.expandedChanged && !update.expanded;
 
             // At this point, the correct bubbles are inflated in the stack.
             // Make sure the order in bubble data is reflected in bubble row.
-            if (update.orderChanged && mStackView != null) {
+            if (update.orderChanged) {
                 mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
                 // if the stack is going to be collapsed, do not update pointer position
                 // after reordering
-                mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
+                mBubbleViewCallback.bubbleOrderChanged(update.bubbles, !collapseStack);
             }
 
             if (collapseStack) {
-                mStackView.setExpanded(false);
+                mBubbleViewCallback.expansionChanged(/* expanded= */ false);
                 mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
             }
 
-            if (update.selectionChanged && mStackView != null) {
-                mStackView.setSelectedBubble(update.selectedBubble);
+            if (update.selectionChanged) {
+                mBubbleViewCallback.selectionChanged(update.selectedBubble);
             }
 
             // Expanding? Apply this last.
             if (update.expandedChanged && update.expanded) {
-                if (mStackView != null) {
-                    mStackView.setExpanded(true);
-                    mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
-                }
+                mBubbleViewCallback.expansionChanged(/* expanded= */ true);
+                mSysuiProxy.requestNotificationShadeTopUi(true, TAG);
             }
 
             mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
@@ -1407,6 +1479,19 @@
         }
     };
 
+    private void updateOverflowButtonDot() {
+        BubbleOverflow overflow = mBubbleData.getOverflow();
+        if (overflow == null) return;
+
+        for (Bubble b : mBubbleData.getOverflowBubbles()) {
+            if (b.showDot()) {
+                overflow.setShowDot(true);
+                return;
+            }
+        }
+        overflow.setShowDot(false);
+    }
+
     private boolean handleDismissalInterception(BubbleEntry entry,
             @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
         if (isSummaryOfBubbles(entry)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 15f8eac..5ecbd6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1360,16 +1360,6 @@
         updateOverflowVisibility();
     }
 
-    void updateOverflowButtonDot() {
-        for (Bubble b : mBubbleData.getOverflowBubbles()) {
-            if (b.showDot()) {
-                mBubbleOverflow.setShowDot(true);
-                return;
-            }
-        }
-        mBubbleOverflow.setShowDot(false);
-    }
-
     /**
      * Handle theme changes.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index ac13f96..f9e0ca5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -247,6 +247,11 @@
             mLaunchRootTask = taskInfo;
         }
 
+        if (mHomeTask != null && mHomeTask.taskId == taskInfo.taskId
+                && !taskInfo.equals(mHomeTask)) {
+            mHomeTask = taskInfo;
+        }
+
         super.onTaskInfoChanged(taskInfo);
     }
 
@@ -364,6 +369,7 @@
         final WindowContainerTransaction wct = getWindowContainerTransaction();
         final Rect taskBounds = calculateBounds();
         wct.setBounds(mLaunchRootTask.token, taskBounds);
+        wct.setBounds(mHomeTask.token, new Rect(0, 0, mDisplayWidth, mDisplayHeight));
         mSyncQueue.queue(wct);
         final SurfaceControl finalLeash = mLaunchRootLeash;
         mSyncQueue.runInSync(t -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 2624ee5..d961d86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -70,4 +70,9 @@
      * Sets the next pip animation type to be the alpha animation.
      */
     oneway void setPipAnimationTypeToAlpha() = 5;
+
+    /**
+     * Sets the height and visibility of the Launcher keep clear area.
+     */
+    oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e9d2571..eb336d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1179,6 +1179,20 @@
     }
 
     /**
+     * Directly update the animator bounds.
+     */
+    public void updateAnimatorBounds(Rect bounds) {
+        final PipAnimationController.PipTransitionAnimator animator =
+                mPipAnimationController.getCurrentAnimator();
+        if (animator != null && animator.isRunning()) {
+            if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
+                animator.updateEndValue(bounds);
+            }
+            animator.setDestinationBounds(bounds);
+        }
+    }
+
+    /**
      * Handles all changes to the PictureInPictureParams.
      */
     protected void applyNewPictureInPictureParams(@NonNull PictureInPictureParams params) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
index c6b5ce9..db6138a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -93,6 +93,11 @@
         return hasEnteredPip(mState);
     }
 
+    /** Returns true if activity is currently entering PiP mode. */
+    public boolean isEnteringPip() {
+        return isEnteringPip(mState);
+    }
+
     public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
         mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
     }
@@ -130,6 +135,11 @@
         return state == ENTERED_PIP;
     }
 
+    /** Returns true if activity is currently entering PiP mode. */
+    public static boolean isEnteringPip(@TransitionState int state) {
+        return state == ENTERING_PIP;
+    }
+
     public interface OnPipTransitionStateChangedListener {
         void onPipTransitionStateChanged(@TransitionState int oldState,
                 @TransitionState int newState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index fa3efeb..0d5f143 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -101,6 +101,7 @@
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -181,14 +182,20 @@
             // early bail out if the keep clear areas feature is disabled
             return;
         }
-        // only move if already in pip, other transitions account for keep clear areas
-        if (mPipTransitionState.hasEnteredPip()) {
+        // only move if we're in PiP or transitioning into PiP
+        if (!mPipTransitionState.shouldBlockResizeRequest()) {
             Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
                     mPipBoundsAlgorithm);
             // only move if the bounds are actually different
             if (destBounds != mPipBoundsState.getBounds()) {
-                mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
-                        mEnterAnimationDuration, null);
+                if (mPipTransitionState.hasEnteredPip()) {
+                    // if already in PiP, schedule separate animation
+                    mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+                            mEnterAnimationDuration, null);
+                } else if (mPipTransitionState.isEnteringPip()) {
+                    // while entering PiP we just need to update animator bounds
+                    mPipTaskOrganizer.updateAnimatorBounds(destBounds);
+                }
             }
         }
     }
@@ -874,6 +881,21 @@
         }
     }
 
+    private void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+        if (visible) {
+            Rect rect = new Rect(
+                    0, mPipBoundsState.getDisplayBounds().bottom - height,
+                    mPipBoundsState.getDisplayBounds().right,
+                    mPipBoundsState.getDisplayBounds().bottom);
+            Set<Rect> restrictedKeepClearAreas = new HashSet<>(
+                    mPipBoundsState.getRestrictedKeepClearAreas());
+            restrictedKeepClearAreas.add(rect);
+            mPipBoundsState.setKeepClearAreas(restrictedKeepClearAreas,
+                    mPipBoundsState.getUnrestrictedKeepClearAreas());
+            updatePipPositionForKeepClearAreas();
+        }
+    }
+
     private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
         mOnIsInPipStateChangedListener = callback;
         if (mOnIsInPipStateChangedListener != null) {
@@ -1237,6 +1259,14 @@
         }
 
         @Override
+        public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+            executeRemoteCallWithTaskPermission(mController, "setLauncherKeepClearAreaHeight",
+                    (controller) -> {
+                        controller.setLauncherKeepClearAreaHeight(visible, height);
+                    });
+        }
+
+        @Override
         public void setPipAnimationListener(IPipAnimationListener listener) {
             executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener",
                     (controller) -> {
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 2f0cb7e..71ee690 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
@@ -207,6 +207,7 @@
     private boolean mIsDividerRemoteAnimating;
     private boolean mIsDropEntering;
     private boolean mIsExiting;
+    private boolean mIsRootTranslucent;
 
     private DefaultMixedHandler mMixedHandler;
     private final Toast mSplitUnsupportedToast;
@@ -422,6 +423,11 @@
             }
         }
 
+        if (!isSplitActive()) {
+            // prevent the fling divider to center transitioni if split screen didn't active.
+            mIsDropEntering = true;
+        }
+
         setSideStagePosition(sideStagePosition, wct);
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         targetStage.evictAllChildren(evictWct);
@@ -436,28 +442,13 @@
         // reparent the task to an invisible split root will make the activity invisible.  Reorder
         // the root task to front to make the entering transition from pip to split smooth.
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, true);
         wct.reorder(targetStage.mRootTaskInfo.token, true);
-        wct.setForceTranslucent(targetStage.mRootTaskInfo.token, true);
-        // prevent the fling divider to center transition
-        mIsDropEntering = true;
-
         targetStage.addTask(task, wct);
 
-        if (ENABLE_SHELL_TRANSITIONS) {
-            prepareEnterSplitScreen(wct);
-            mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct,
-                    null, this, null /* consumedCallback */, (finishWct, finishT) -> {
-                        if (!evictWct.isEmpty()) {
-                            finishWct.merge(evictWct, true);
-                        }
-                    } /* finishedCallback */);
-        } else {
-            if (!evictWct.isEmpty()) {
-                wct.merge(evictWct, true /* transfer */);
-            }
-            mTaskOrganizer.applyTransaction(wct);
+        if (!evictWct.isEmpty()) {
+            wct.merge(evictWct, true /* transfer */);
         }
+        mTaskOrganizer.applyTransaction(wct);
         return true;
     }
 
@@ -716,7 +707,7 @@
         mSplitLayout.setDivideRatio(splitRatio);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
 
         // Make sure the launch options will put tasks in the corresponding split roots
         mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -923,7 +914,7 @@
 
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
 
         // TODO(b/268008375): Merge APIs to start a split pair into one.
         if (mainTaskId != INVALID_TASK_ID) {
@@ -1344,7 +1335,7 @@
             mSideStage.removeAllTasks(wct, false /* toTop */);
             mMainStage.deactivate(wct, false /* toTop */);
             wct.reorder(mRootTaskInfo.token, false /* onTop */);
-            wct.setForceTranslucent(mRootTaskInfo.token, true);
+            setRootForceTranslucent(true, wct);
             wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
             onTransitionAnimationComplete();
         } else {
@@ -1376,7 +1367,7 @@
                     mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
                     mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
                     finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
-                    finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+                    setRootForceTranslucent(true, wct);
                     finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
                     mSyncQueue.queue(finishedWCT);
                     mSyncQueue.runInSync(at -> {
@@ -1488,7 +1479,7 @@
         mMainStage.activate(wct, true /* includingTopTask */);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
+        setRootForceTranslucent(false, wct);
     }
 
     void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1692,6 +1683,7 @@
 
         mRootTaskInfo = null;
         mRootTaskLeash = null;
+        mIsRootTranslucent = false;
     }
 
 
@@ -1710,7 +1702,7 @@
         // Make the stages adjacent to each other so they occlude what's behind them.
         wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
         wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
-        wct.setForceTranslucent(mRootTaskInfo.token, true);
+        setRootForceTranslucent(true, wct);
         mSplitLayout.getInvisibleBounds(mTempRect1);
         wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
         mSyncQueue.queue(wct);
@@ -1734,7 +1726,7 @@
             mSideStage.evictOtherChildren(wct, taskId);
             updateWindowBounds(mSplitLayout, wct);
             wct.reorder(mRootTaskInfo.token, true);
-            wct.setForceTranslucent(mRootTaskInfo.token, false);
+            setRootForceTranslucent(false, wct);
 
             mSyncQueue.queue(wct);
             mSyncQueue.runInSync(t -> {
@@ -1758,6 +1750,13 @@
         mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout);
     }
 
+    private void setRootForceTranslucent(boolean translucent, WindowContainerTransaction wct) {
+        if (mIsRootTranslucent == translucent) return;
+
+        mIsRootTranslucent = translucent;
+        wct.setForceTranslucent(mRootTaskInfo.token, translucent);
+    }
+
     private void onStageVisibilityChanged(StageListenerImpl stageListener) {
         // If split didn't active, just ignore this callback because we should already did these
         // on #applyExitSplitScreen.
@@ -1784,12 +1783,11 @@
             // Split entering background.
             wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
                     true /* setReparentLeafTaskIfRelaunch */);
-            if (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping) {
-                wct.setForceTranslucent(mRootTaskInfo.token, true);
-            }
+            setRootForceTranslucent(true, wct);
         } else {
             wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
                     false /* setReparentLeafTaskIfRelaunch */);
+            setRootForceTranslucent(false, wct);
         }
 
         mSyncQueue.queue(wct);
@@ -1921,7 +1919,7 @@
                 mMainStage.activate(wct, true /* includingTopTask */);
                 updateWindowBounds(mSplitLayout, wct);
                 wct.reorder(mRootTaskInfo.token, true);
-                wct.setForceTranslucent(mRootTaskInfo.token, false);
+                setRootForceTranslucent(false, wct);
             }
 
             mSyncQueue.queue(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index a3d364a..0bce3ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,6 +40,7 @@
     private final DisplayController mDisplayController;
     private final WindowDecoration mWindowDecoration;
 
+    private final Rect mTempBounds = new Rect();
     private final Rect mTaskBoundsAtDragStart = new Rect();
     private final PointF mRepositionStartPoint = new PointF();
     private final Rect mRepositionTaskBounds = new Rect();
@@ -117,17 +118,32 @@
         final float deltaX = x - mRepositionStartPoint.x;
         final float deltaY = y - mRepositionStartPoint.y;
         mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
+
+        final Rect stableBounds = mTempBounds;
+        // Make sure the new resizing destination in any direction falls within the stable bounds.
+        // If not, set the bounds back to the old location that was valid to avoid conflicts with
+        // some regions such as the gesture area.
+        mDisplayController.getDisplayLayout(mWindowDecoration.mDisplay.getDisplayId())
+                .getStableBounds(stableBounds);
         if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
-            mRepositionTaskBounds.left += deltaX;
+            final int candidateLeft = mRepositionTaskBounds.left + (int) deltaX;
+            mRepositionTaskBounds.left = (candidateLeft > stableBounds.left)
+                    ? candidateLeft : oldLeft;
         }
         if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
-            mRepositionTaskBounds.right += deltaX;
+            final int candidateRight = mRepositionTaskBounds.right + (int) deltaX;
+            mRepositionTaskBounds.right = (candidateRight < stableBounds.right)
+                    ? candidateRight : oldRight;
         }
         if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
-            mRepositionTaskBounds.top += deltaY;
+            final int candidateTop = mRepositionTaskBounds.top + (int) deltaY;
+            mRepositionTaskBounds.top = (candidateTop > stableBounds.top)
+                    ? candidateTop : oldTop;
         }
         if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
-            mRepositionTaskBounds.bottom += deltaY;
+            final int candidateBottom = mRepositionTaskBounds.bottom + (int) deltaY;
+            mRepositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
+                    ? candidateBottom : oldBottom;
         }
         if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             mRepositionTaskBounds.offset((int) deltaX, (int) deltaY);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
index 8f66f4e..94c064b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -5,13 +5,16 @@
 import android.graphics.Rect
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
+import android.view.Display
 import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_BOTTOM
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_TOP
 import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
@@ -19,10 +22,11 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
 import org.mockito.Mockito.argThat
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 /**
@@ -51,6 +55,8 @@
     private lateinit var mockDisplayController: DisplayController
     @Mock
     private lateinit var mockDisplayLayout: DisplayLayout
+    @Mock
+    private lateinit var mockDisplay: Display
 
     private lateinit var taskPositioner: TaskPositioner
 
@@ -68,6 +74,9 @@
         `when`(taskToken.asBinder()).thenReturn(taskBinder)
         `when`(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
         `when`(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
+        `when`(mockDisplayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(STABLE_BOUNDS)
+        }
 
         mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
             taskId = TASK_ID
@@ -78,6 +87,8 @@
             displayId = DISPLAY_ID
             configuration.windowConfiguration.bounds = STARTING_BOUNDS
         }
+        mockWindowDecoration.mDisplay = mockDisplay
+        `when`(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
     }
 
     @Test
@@ -451,6 +462,72 @@
         })
     }
 
+    fun testDragResize_toDisallowedBounds_freezesAtLimit() {
+        taskPositioner.onDragPositioningStart(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, // Resize right-bottom corner
+                STARTING_BOUNDS.right.toFloat(),
+                STARTING_BOUNDS.bottom.toFloat()
+        )
+
+        // Resize the task by 10px to the right and bottom, a valid destination
+        val newBounds = Rect(
+                STARTING_BOUNDS.left,
+                STARTING_BOUNDS.top,
+                STARTING_BOUNDS.right + 10,
+                STARTING_BOUNDS.bottom + 10)
+        taskPositioner.onDragPositioningMove(
+                newBounds.right.toFloat(),
+                newBounds.bottom.toFloat()
+        )
+
+        // Resize the task by another 10px to the right (allowed) and to just in the disallowed
+        // area of the Y coordinate.
+        val newBounds2 = Rect(
+                newBounds.left,
+                newBounds.top,
+                newBounds.right + 10,
+                DISALLOWED_RESIZE_AREA.top
+        )
+        taskPositioner.onDragPositioningMove(
+                newBounds2.right.toFloat(),
+                newBounds2.bottom.toFloat()
+        )
+
+        taskPositioner.onDragPositioningEnd(newBounds2.right.toFloat(), newBounds2.bottom.toFloat())
+
+        // The first resize falls in the allowed area, verify there's a change for it.
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(newBounds)
+            }
+        })
+        // The second resize falls in the disallowed area, verify there's no change for it.
+        verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(newBounds2)
+            }
+        })
+        // Instead, there should be a change for its allowed portion (the X movement) with the Y
+        // staying frozen in the last valid resize position.
+        verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+            return@argThat wct.changes.any { (token, change) ->
+                token == taskBinder && change.ofBounds(
+                        Rect(
+                                newBounds2.left,
+                                newBounds2.top,
+                                newBounds2.right,
+                                newBounds.bottom // Stayed at the first resize destination.
+                        )
+                )
+            }
+        })
+    }
+
+    private fun WindowContainerTransaction.Change.ofBounds(bounds: Rect): Boolean {
+        return ((windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) &&
+                bounds == configuration.windowConfiguration.bounds
+    }
+
     companion object {
         private const val TASK_ID = 5
         private const val MIN_WIDTH = 10
@@ -458,6 +535,19 @@
         private const val DENSITY_DPI = 20
         private const val DEFAULT_MIN = 40
         private const val DISPLAY_ID = 1
+        private const val NAVBAR_HEIGHT = 50
+        private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
         private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+        private val DISALLOWED_RESIZE_AREA = Rect(
+                DISPLAY_BOUNDS.left,
+                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
+                DISPLAY_BOUNDS.right,
+                DISPLAY_BOUNDS.bottom)
+        private val STABLE_BOUNDS = Rect(
+                DISPLAY_BOUNDS.left,
+                DISPLAY_BOUNDS.top,
+                DISPLAY_BOUNDS.right,
+                DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
+        )
     }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 2903288..f0a8211 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -19,14 +19,15 @@
 import android.graphics.fonts.Font
 import android.graphics.fonts.FontVariationAxis
 import android.util.MathUtils
+import android.util.MathUtils.abs
+import java.lang.Float.max
+import java.lang.Float.min
 
 private const val TAG_WGHT = "wght"
 private const val TAG_ITAL = "ital"
 
-private const val FONT_WEIGHT_MAX = 1000f
-private const val FONT_WEIGHT_MIN = 0f
-private const val FONT_WEIGHT_ANIMATION_STEP = 10f
 private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
+private const val FONT_WEIGHT_ANIMATION_FRAME_COUNT = 100
 
 private const val FONT_ITALIC_MAX = 1f
 private const val FONT_ITALIC_MIN = 0f
@@ -118,14 +119,17 @@
             lerp(startAxes, endAxes) { tag, startValue, endValue ->
                 when (tag) {
                     // TODO: Good to parse 'fvar' table for retrieving default value.
-                    TAG_WGHT ->
-                        adjustWeight(
+                    TAG_WGHT -> {
+                        adaptiveAdjustWeight(
                             MathUtils.lerp(
                                 startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                                 endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                                 progress
-                            )
+                            ),
+                            startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
+                            endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                         )
+                    }
                     TAG_ITAL ->
                         adjustItalic(
                             MathUtils.lerp(
@@ -205,10 +209,14 @@
         return result
     }
 
-    // For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
+    // For the performance reasons, we animate weight with adaptive step. This helps
     // Cache hit ratio in the Skia glyph cache.
-    private fun adjustWeight(value: Float) =
-        coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+    // The reason we don't use fix step is because the range of weight axis is not normalized,
+    // some are from 50 to 100, others are from 0 to 1000, so we cannot give a constant proper step
+    private fun adaptiveAdjustWeight(value: Float, start: Float, end: Float): Float {
+        val step = max(abs(end - start) / FONT_WEIGHT_ANIMATION_FRAME_COUNT, 1F)
+        return coerceInWithStep(value, min(start, end), max(start, end), step)
+    }
 
     // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
     // Cache hit ratio in the Skia glyph cache.
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index a08b598..7fe94d3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -190,6 +190,7 @@
      * @param textSize an optional font size.
      * @param colors an optional colors array that must be the same size as numLines passed to
      *               the TextInterpolator
+     * @param strokeWidth an optional paint stroke width
      * @param animate an optional boolean indicating true for showing style transition as animation,
      *                false for immediate style transition. True by default.
      * @param duration an optional animation duration in milliseconds. This is ignored if animate is
@@ -201,6 +202,7 @@
         weight: Int = -1,
         textSize: Float = -1f,
         color: Int? = null,
+        strokeWidth: Float = -1f,
         animate: Boolean = true,
         duration: Long = -1L,
         interpolator: TimeInterpolator? = null,
@@ -254,6 +256,9 @@
         if (color != null) {
             textInterpolator.targetPaint.color = color
         }
+        if (strokeWidth >= 0F) {
+            textInterpolator.targetPaint.strokeWidth = strokeWidth
+        }
         textInterpolator.onTargetPaintModified()
 
         if (animate) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 468a8b1..3eb7fd8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -473,6 +473,7 @@
         // TODO(172943390): Add other interpolation or support custom interpolator.
         out.textSize = MathUtils.lerp(from.textSize, to.textSize, progress)
         out.color = ColorUtils.blendARGB(from.color, to.color, progress)
+        out.strokeWidth = MathUtils.lerp(from.strokeWidth, to.strokeWidth, progress)
     }
 
     // Shape the text and stores the result to out argument.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 359da13..5b27c40 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -29,8 +29,11 @@
 import android.annotation.DrawableRes;
 import android.annotation.SuppressLint;
 import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -86,6 +89,7 @@
     private RotationButton mRotationButton;
 
     private boolean mIsRecentsAnimationRunning;
+    private boolean mDocked;
     private boolean mHomeRotationEnabled;
     private int mLastRotationSuggestion;
     private boolean mPendingRotationSuggestion;
@@ -123,6 +127,12 @@
             () -> mPendingRotationSuggestion = false;
     private Animator mRotateHideAnimator;
 
+    private final BroadcastReceiver mDockedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateDockedState(intent);
+        }
+    };
 
     private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
         @Override
@@ -136,7 +146,8 @@
                 // The isVisible check makes the rotation button disappear when we are not locked
                 // (e.g. for tabletop auto-rotate).
                 if (rotationLocked || mRotationButton.isVisible()) {
-                    if (shouldOverrideUserLockPrefs(rotation) && rotationLocked) {
+                    // Do not allow a change in rotation to set user rotation when docked.
+                    if (shouldOverrideUserLockPrefs(rotation) && rotationLocked && !mDocked) {
                         setRotationLockedAtAngle(rotation);
                     }
                     setRotateSuggestionButtonState(false /* visible */, true /* forced */);
@@ -214,6 +225,10 @@
         }
 
         mListenersRegistered = true;
+
+        updateDockedState(mContext.registerReceiver(mDockedReceiver,
+                new IntentFilter(Intent.ACTION_DOCK_EVENT)));
+
         try {
             WindowManagerGlobal.getWindowManagerService()
                     .watchRotation(mRotationWatcher, DEFAULT_DISPLAY);
@@ -234,6 +249,8 @@
         }
 
         mListenersRegistered = false;
+
+        mContext.unregisterReceiver(mDockedReceiver);
         try {
             WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher);
         } catch (RemoteException e) {
@@ -345,6 +362,15 @@
         updateRotationButtonStateInOverview();
     }
 
+    private void updateDockedState(Intent intent) {
+        if (intent == null) {
+            return;
+        }
+
+        mDocked = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED)
+                != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+    }
+
     private void updateRotationButtonStateInOverview() {
         if (mIsRecentsAnimationRunning && !mHomeRotationEnabled) {
             setRotateSuggestionButtonState(false, true /* hideImmediately */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2f937a9..ba5a8c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,6 +39,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -1067,10 +1068,14 @@
 
             int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
 
+            AnimatorSet anims = new AnimatorSet();
             ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
-            yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
-            yAnim.setDuration(500);
-            yAnim.start();
+            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
+                    0f);
+
+            anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+            anims.playTogether(alphaAnim, yAnim);
+            anims.start();
         }
 
         private void setupUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index c32b853..2c2caea 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -127,6 +127,7 @@
     private View.OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event);
     private ActivityStarter.OnDismissAction mDismissAction;
     private Runnable mCancelAction;
+    private boolean mWillRunDismissFromKeyguard;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
 
@@ -262,8 +263,10 @@
             // If there's a pending runnable because the user interacted with a widget
             // and we're leaving keyguard, then run it.
             boolean deferKeyguardDone = false;
+            mWillRunDismissFromKeyguard = false;
             if (mDismissAction != null) {
                 deferKeyguardDone = mDismissAction.onDismiss();
+                mWillRunDismissFromKeyguard = mDismissAction.willRunAnimationOnKeyguard();
                 mDismissAction = null;
                 mCancelAction = null;
             }
@@ -526,6 +529,13 @@
     }
 
     /**
+     * @return will the dismissal run from the keyguard layout (instead of from bouncer)
+     */
+    public boolean willRunDismissFromKeyguard() {
+        return mWillRunDismissFromKeyguard;
+    }
+
+    /**
      * Remove any dismiss action or cancel action that was set.
      */
     public void cancelDismissAction() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b2f4c30..d83af60 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2439,8 +2439,10 @@
                 () -> mFaceManager != null && mFaceManager.isHardwareDetected()
                         && mFaceManager.hasEnrolledTemplates(userId)
                         && mBiometricEnabledForUser.get(userId));
+        if (mIsFaceEnrolled != isFaceEnrolled) {
+            mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
+        }
         mIsFaceEnrolled = isFaceEnrolled;
-        mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
     }
 
     public boolean isFaceSupported() {
@@ -3100,12 +3102,13 @@
     @VisibleForTesting
     boolean isUnlockWithFingerprintPossible(int userId) {
         // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        boolean fpEnrolled = mFpm != null && mFpm.isHardwareDetected()
+        boolean newFpEnrolled = mFpm != null && mFpm.isHardwareDetected()
                 && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
-        mLogger.logFpEnrolledUpdated(userId,
-                mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
-                fpEnrolled);
-        mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
+        Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
+        if (oldFpEnrolled != newFpEnrolled) {
+            mLogger.logFpEnrolledUpdated(userId, oldFpEnrolled, newFpEnrolled);
+        }
+        mIsUnlockWithFingerprintPossible.put(userId, newFpEnrolled);
         return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b8de3f1..999dd48 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -143,7 +143,7 @@
      * the digits when the clock moves.
      */
     @JvmField
-    val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true)
+    val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation")
 
     /**
      * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
@@ -488,6 +488,13 @@
     val ENABLE_PIP_APP_ICON_OVERLAY =
         sysPropBooleanFlag(1115, "persist.wm.debug.enable_pip_app_icon_overlay", default = true)
 
+    // TODO(b/272110828): Tracking bug
+    @Keep
+    @JvmField
+    val ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
+        sysPropBooleanFlag(
+            1116, "persist.wm.debug.enable_move_floating_window_in_tabletop", default = false)
+
     // 1200 - predictive back
     @Keep
     @JvmField
@@ -596,7 +603,7 @@
     @JvmField
     val LEAVE_SHADE_OPEN_FOR_BUGREPORT = releasedFlag(1800, "leave_shade_open_for_bugreport")
     // TODO(b/265944639): Tracking Bug
-    @JvmField val DUAL_SHADE = releasedFlag(1801, "dual_shade")
+    @JvmField val DUAL_SHADE = unreleasedFlag(1801, "dual_shade")
 
     // 1900
     @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
@@ -672,5 +679,5 @@
     // TODO(b/272036292): Tracking Bug
     @JvmField
     val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
-            unreleasedFlag(2602, "large_shade_granular_alpha_interpolation")
+            unreleasedFlag(2602, "large_shade_granular_alpha_interpolation", teamfood = true)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 8ae171f..90562dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -380,10 +380,7 @@
                 // If the launcher is underneath, but we're about to launch an activity, don't do
                 // the animations since they won't be visible.
                 !notificationShadeWindowController.isLaunchingActivity &&
-                launcherUnlockController != null &&
-                // Temporarily disable for foldables since foldable launcher has two first pages,
-                // which breaks the in-window animation.
-                !isFoldable(context)
+                launcherUnlockController != null
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
index faeb485..a2589d3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
@@ -52,6 +52,7 @@
         cancelAction: Runnable?,
     )
     fun willDismissWithActions(): Boolean
+    fun willRunDismissFromKeyguard(): Boolean
     /** @return the {@link OnBackAnimationCallback} to animate Bouncer during a back gesture. */
     fun getBackCallback(): OnBackAnimationCallback
 }
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/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 2dc8fee..1fbfff9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -47,6 +47,7 @@
         listenForOccludedToLockscreen()
         listenForOccludedToDreaming()
         listenForOccludedToAodOrDozing()
+        listenForOccludedToGone()
     }
 
     private fun listenForOccludedToDreaming() {
@@ -72,11 +73,22 @@
     private fun listenForOccludedToLockscreen() {
         scope.launch {
             keyguardInteractor.isKeyguardOccluded
-                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (isOccluded, lastStartedKeyguardState) ->
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
                     // Occlusion signals come from the framework, and should interrupt any
                     // existing transition
-                    if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
+                    if (
+                        !isOccluded &&
+                            isShowing &&
+                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
+                    ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
@@ -90,6 +102,38 @@
         }
     }
 
+    private fun listenForOccludedToGone() {
+        scope.launch {
+            keyguardInteractor.isKeyguardOccluded
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
+                    // Occlusion signals come from the framework, and should interrupt any
+                    // existing transition
+                    if (
+                        !isOccluded &&
+                            !isShowing &&
+                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.OCCLUDED,
+                                KeyguardState.GONE,
+                                getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
     private fun listenForOccludedToAodOrDozing() {
         scope.launch {
             keyguardInteractor.wakefulnessModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 6d8a849..a263562 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -380,6 +380,11 @@
         return primaryBouncerView.delegate?.willDismissWithActions() == true
     }
 
+    /** Will the dismissal run from the keyguard layout (instead of from bouncer) */
+    fun willRunDismissFromKeyguard(): Boolean {
+        return primaryBouncerView.delegate?.willRunDismissFromKeyguard() == true
+    }
+
     /** Returns whether the bouncer should be full screen. */
     private fun needsFullscreenBouncer(): Boolean {
         val mode: KeyguardSecurityModel.SecurityMode =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScrimAlpha.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScrimAlpha.kt
new file mode 100644
index 0000000..1db7733
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScrimAlpha.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+/** Alpha values for scrim updates */
+data class ScrimAlpha(
+    val frontAlpha: Float = 0f,
+    val behindAlpha: Float = 0f,
+    val notificationsAlpha: Float = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 2337ffc..bb617bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -97,6 +97,10 @@
                 override fun willDismissWithActions(): Boolean {
                     return securityContainerController.hasDismissActions()
                 }
+
+                override fun willRunDismissFromKeyguard(): Boolean {
+                    return securityContainerController.willRunDismissFromKeyguard()
+                }
             }
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 92038e2..b23247c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -20,11 +20,14 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
 
 /**
  * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
@@ -36,6 +39,7 @@
 constructor(
     private val interactor: KeyguardTransitionInteractor,
     private val statusBarStateController: SysuiStatusBarStateController,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
 ) {
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
@@ -44,26 +48,49 @@
         )
 
     private var leaveShadeOpen: Boolean = false
+    private var willRunDismissFromKeyguard: Boolean = false
 
     /** Bouncer container alpha */
     val bouncerAlpha: Flow<Float> =
         transitionAnimation.createFlow(
             duration = 200.milliseconds,
-            onStep = { 1f - it },
-        )
-
-    /** Scrim behind alpha */
-    val scrimBehindAlpha: Flow<Float> =
-        transitionAnimation.createFlow(
-            duration = TO_GONE_DURATION,
-            interpolator = EMPHASIZED_ACCELERATE,
-            onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
+            onStart = {
+                willRunDismissFromKeyguard = primaryBouncerInteractor.willRunDismissFromKeyguard()
+            },
             onStep = {
-                if (leaveShadeOpen) {
-                    1f
+                if (willRunDismissFromKeyguard) {
+                    0f
                 } else {
                     1f - it
                 }
             },
         )
+
+    /** Scrim alpha values */
+    val scrimAlpha: Flow<ScrimAlpha> =
+        transitionAnimation
+            .createFlow(
+                duration = TO_GONE_DURATION,
+                interpolator = EMPHASIZED_ACCELERATE,
+                onStart = {
+                    leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
+                    willRunDismissFromKeyguard =
+                        primaryBouncerInteractor.willRunDismissFromKeyguard()
+                },
+                onStep = { 1f - it },
+            )
+            .map {
+                if (willRunDismissFromKeyguard) {
+                    ScrimAlpha(
+                        notificationsAlpha = 1f,
+                    )
+                } else if (leaveShadeOpen) {
+                    ScrimAlpha(
+                        behindAlpha = 1f,
+                        notificationsAlpha = 1f,
+                    )
+                } else {
+                    ScrimAlpha(behindAlpha = it)
+                }
+            }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 55cb5d3..1818dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -153,7 +153,6 @@
     // We don't correctly track dark mode until the content views are inflated, so always update
     // the background on first content update just in case it happens to be during a theme change.
     private boolean mUpdateSelfBackgroundOnUpdate = true;
-    private boolean mNotificationTranslationFinished = false;
     private boolean mIsSnoozed;
     private boolean mIsFaded;
     private boolean mAnimatePinnedRoundness = false;
@@ -209,11 +208,6 @@
      */
     private boolean mUserExpanded;
     /**
-     * Whether the blocking helper is showing on this notification (even if dismissed)
-     */
-    private boolean mIsBlockingHelperShowing;
-
-    /**
      * Has this notification been expanded while it was pinned
      */
     private boolean mExpandedWhenPinned;
@@ -1565,18 +1559,6 @@
         }
     }
 
-    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
-        mIsBlockingHelperShowing = isBlockingHelperShowing;
-    }
-
-    public boolean isBlockingHelperShowing() {
-        return mIsBlockingHelperShowing;
-    }
-
-    public boolean isBlockingHelperShowingAndTranslationFinished() {
-        return mIsBlockingHelperShowing && mNotificationTranslationFinished;
-    }
-
     @Override
     public View getShelfTransformationTarget() {
         if (mIsSummaryWithChildren && !shouldShowPublic()) {
@@ -2155,10 +2137,7 @@
     @Override
     public void setTranslation(float translationX) {
         invalidate();
-        if (isBlockingHelperShowingAndTranslationFinished()) {
-            mGuts.setTranslationX(translationX);
-            return;
-        } else if (mDismissUsingRowTranslationX) {
+        if (mDismissUsingRowTranslationX) {
             setTranslationX(translationX);
         } else if (mTranslateableViews != null) {
             // Translate the group of views
@@ -2186,10 +2165,6 @@
             return getTranslationX();
         }
 
-        if (isBlockingHelperShowingAndCanTranslate()) {
-            return mGuts.getTranslationX();
-        }
-
         if (mTranslateableViews != null && mTranslateableViews.size() > 0) {
             // All of the views in the list should have same translation, just use first one.
             return mTranslateableViews.get(0).getTranslationX();
@@ -2198,10 +2173,6 @@
         return 0;
     }
 
-    private boolean isBlockingHelperShowingAndCanTranslate() {
-        return areGutsExposed() && mIsBlockingHelperShowing && mNotificationTranslationFinished;
-    }
-
     public Animator getTranslateViewAnimator(final float leftTarget,
                                              AnimatorUpdateListener listener) {
         if (mTranslateAnim != null) {
@@ -2223,9 +2194,6 @@
 
             @Override
             public void onAnimationEnd(Animator anim) {
-                if (mIsBlockingHelperShowing) {
-                    mNotificationTranslationFinished = true;
-                }
                 if (!cancelled && leftTarget == 0) {
                     if (mMenuRow != null) {
                         mMenuRow.resetMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 93f0812..596bdc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -238,12 +238,11 @@
     }
 
     public void openControls(
-            boolean shouldDoCircularReveal,
             int x,
             int y,
             boolean needsFalsingProtection,
             @Nullable Runnable onAnimationEnd) {
-        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
+        animateOpen(x, y, onAnimationEnd);
         setExposed(true /* exposed */, needsFalsingProtection);
     }
 
@@ -300,7 +299,7 @@
         if (mGutsContent == null
                 || !mGutsContent.handleCloseControls(save, force)) {
             // We only want to do a circular reveal if we're not showing the blocking helper.
-            animateClose(x, y, true /* shouldDoCircularReveal */);
+            animateClose(x, y);
 
             setExposed(false, mNeedsFalsingProtection);
             if (mClosedListener != null) {
@@ -309,66 +308,45 @@
         }
     }
 
-    /** Animates in the guts view via either a fade or a circular reveal. */
-    private void animateOpen(
-            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
+    /** Animates in the guts view with a circular reveal. */
+    private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
         if (isAttachedToWindow()) {
-            if (shouldDoCircularReveal) {
-                double horz = Math.max(getWidth() - x, x);
-                double vert = Math.max(getHeight() - y, y);
-                float r = (float) Math.hypot(horz, vert);
-                // Make sure we'll be visible after the circular reveal
-                setAlpha(1f);
-                // Circular reveal originating at (x, y)
-                Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
-                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-                a.addListener(new AnimateOpenListener(onAnimationEnd));
-                a.start();
-            } else {
-                // Fade in content
-                this.setAlpha(0f);
-                this.animate()
-                        .alpha(1f)
-                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
-                        .setInterpolator(Interpolators.ALPHA_IN)
-                        .setListener(new AnimateOpenListener(onAnimationEnd))
-                        .start();
-            }
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            // Make sure we'll be visible after the circular reveal
+            setAlpha(1f);
+            // Circular reveal originating at (x, y)
+            Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            a.addListener(new AnimateOpenListener(onAnimationEnd));
+            a.start();
+
         } else {
             Log.w(TAG, "Failed to animate guts open");
         }
     }
 
 
-    /** Animates out the guts view via either a fade or a circular reveal. */
+    /** Animates out the guts view with a circular reveal. */
     @VisibleForTesting
-    void animateClose(int x, int y, boolean shouldDoCircularReveal) {
+    void animateClose(int x, int y) {
         if (isAttachedToWindow()) {
-            if (shouldDoCircularReveal) {
-                // Circular reveal originating at (x, y)
-                if (x == -1 || y == -1) {
-                    x = (getLeft() + getRight()) / 2;
-                    y = (getTop() + getHeight() / 2);
-                }
-                double horz = Math.max(getWidth() - x, x);
-                double vert = Math.max(getHeight() - y, y);
-                float r = (float) Math.hypot(horz, vert);
-                Animator a = ViewAnimationUtils.createCircularReveal(this,
-                        x, y, r, 0);
-                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-                a.addListener(new AnimateCloseListener(this /* view */, mGutsContent));
-                a.start();
-            } else {
-                // Fade in the blocking helper.
-                this.animate()
-                        .alpha(0f)
-                        .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
-                        .setInterpolator(Interpolators.ALPHA_OUT)
-                        .setListener(new AnimateCloseListener(this, /* view */mGutsContent))
-                        .start();
+            // Circular reveal originating at (x, y)
+            if (x == -1 || y == -1) {
+                x = (getLeft() + getRight()) / 2;
+                y = (getTop() + getHeight() / 2);
             }
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            Animator a = ViewAnimationUtils.createCircularReveal(this,
+                    x, y, r, 0);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+            a.addListener(new AnimateCloseListener(this /* view */, mGutsContent));
+            a.start();
         } else {
             Log.w(TAG, "Failed to animate guts close");
             mGutsContent.onFinishedClosing();
@@ -449,7 +427,7 @@
         return mGutsContent != null && mGutsContent.isLeavebehind();
     }
 
-    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
+    /** Listener for animations executed in {@link #animateOpen(int, int, Runnable)}. */
     private static class AnimateOpenListener extends AnimatorListenerAdapter {
         final Runnable mOnAnimationEnd;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 06d4080..46f1bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -629,7 +629,6 @@
                                 !mAccessibilityManager.isTouchExplorationEnabled());
 
                 guts.openControls(
-                        !row.isBlockingHelperShowing(),
                         x,
                         y,
                         needsFalsingProtection,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 56d1c08..f5d2eee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -59,13 +59,13 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.ScrimAlpha;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -209,7 +209,6 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private final SysuiStatusBarStateController mStatusBarStateController;
 
     private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
@@ -270,12 +269,16 @@
     private CoroutineDispatcher mMainDispatcher;
     private boolean mIsBouncerToGoneTransitionRunning = false;
     private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-    private final Consumer<Float> mScrimAlphaConsumer =
-            (Float alpha) -> {
+    private final Consumer<ScrimAlpha> mScrimAlphaConsumer =
+            (ScrimAlpha alphas) -> {
+                mInFrontAlpha = alphas.getFrontAlpha();
                 mScrimInFront.setViewAlpha(mInFrontAlpha);
+
+                mNotificationsAlpha = alphas.getNotificationsAlpha();
                 mNotificationsScrim.setViewAlpha(mNotificationsAlpha);
-                mBehindAlpha = alpha;
-                mScrimBehind.setViewAlpha(alpha);
+
+                mBehindAlpha = alphas.getBehindAlpha();
+                mScrimBehind.setViewAlpha(mBehindAlpha);
             };
 
     Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
@@ -297,7 +300,6 @@
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
-            SysuiStatusBarStateController sysuiStatusBarStateController,
             @Main CoroutineDispatcher mainDispatcher,
             LargeScreenShadeInterpolator largeScreenShadeInterpolator,
             FeatureFlags featureFlags) {
@@ -309,7 +311,6 @@
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mStatusBarStateController = sysuiStatusBarStateController;
         mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
         mHandler = handler;
         mMainExecutor = mainExecutor;
@@ -409,7 +410,7 @@
 
         collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
                 mPrimaryBouncerToGoneTransition, mMainDispatcher);
-        collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha(),
+        collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
                 mScrimAlphaConsumer, mMainDispatcher);
     }
 
@@ -1114,8 +1115,7 @@
             mBehindAlpha = 1;
         }
         // Prevent notification scrim flicker when transitioning away from keyguard.
-        if (mKeyguardStateController.isKeyguardGoingAway()
-                && !mStatusBarStateController.leaveOpenOnKeyguardHide()) {
+        if (mKeyguardStateController.isKeyguardGoingAway()) {
             mNotificationsAlpha = 0;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3c32131..b6a3ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -733,7 +733,9 @@
             } else {
                 showBouncerOrKeyguard(hideBouncerWhenShowing);
             }
-            hideAlternateBouncer(false);
+            if (hideBouncerWhenShowing) {
+                hideAlternateBouncer(false);
+            }
             mKeyguardUpdateManager.sendKeyguardReset();
             updateStates();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 078a00d..189f2e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -27,6 +27,7 @@
 import android.app.TaskStackBuilder;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
@@ -45,6 +46,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.EventLogTags;
@@ -74,6 +76,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.wmshell.BubblesManager;
 
+import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
@@ -81,6 +84,7 @@
 
 import dagger.Lazy;
 
+
 /**
  * Status bar implementation of {@link NotificationActivityStarter}.
  */
@@ -572,16 +576,29 @@
         });
 
         // not immersive & a fullscreen alert should be shown
-        final PendingIntent fullscreenIntent =
+        final PendingIntent fullScreenIntent =
                 entry.getSbn().getNotification().fullScreenIntent;
-        mLogger.logSendingFullScreenIntent(entry, fullscreenIntent);
+        mLogger.logSendingFullScreenIntent(entry, fullScreenIntent);
         try {
             EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
                     entry.getKey());
             mCentralSurfaces.wakeUpForFullScreenIntent();
-            fullscreenIntent.send();
+            fullScreenIntent.send();
             entry.notifyFullScreenIntentLaunched();
             mMetricsLogger.count("note_fullscreen", 1);
+
+            String activityName;
+            List<ResolveInfo> resolveInfos = fullScreenIntent.queryIntentComponents(0);
+            if (resolveInfos.size() > 0 && resolveInfos.get(0) != null
+                    && resolveInfos.get(0).activityInfo != null
+                    && resolveInfos.get(0).activityInfo.name != null) {
+                activityName = resolveInfos.get(0).activityInfo.name;
+            } else {
+                activityName = "";
+            }
+            FrameworkStatsLog.write(FrameworkStatsLog.FULL_SCREEN_INTENT_LAUNCHED,
+                    fullScreenIntent.getCreatorUid(),
+                    activityName);
         } catch (PendingIntent.CanceledException e) {
             // ignore
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index b847d67..1cfcf8c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -1473,6 +1473,7 @@
         mHandler.removeMessages(H.SHOW);
         if (mIsAnimatingDismiss) {
             Log.d(TAG, "dismissH: isAnimatingDismiss");
+            Trace.endSection();
             return;
         }
         mIsAnimatingDismiss = true;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index bbc7bc9..4dc4c2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -519,6 +519,38 @@
     }
 
     @Test
+    public void testWillRunDismissFromKeyguardIsTrue() {
+        ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class);
+        when(action.willRunAnimationOnKeyguard()).thenReturn(true);
+        mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */);
+
+        mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
+
+        assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isTrue();
+    }
+
+    @Test
+    public void testWillRunDismissFromKeyguardIsFalse() {
+        ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class);
+        when(action.willRunAnimationOnKeyguard()).thenReturn(false);
+        mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */);
+
+        mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
+
+        assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse();
+    }
+
+    @Test
+    public void testWillRunDismissFromKeyguardIsFalseWhenNoDismissActionSet() {
+        mKeyguardSecurityContainerController.setOnDismissAction(null /* action */,
+                null /* cancelAction */);
+
+        mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
+
+        assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse();
+    }
+
+    @Test
     public void testOnStartingToHide() {
         mKeyguardSecurityContainerController.onStartingToHide();
         verify(mInputViewController).onStartingToHide();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index f01da2d..8a5c5b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -61,7 +61,7 @@
         val interp = FontInterpolator()
         assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
         assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f))
-        assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
+        assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f))
     }
 
     @Test
@@ -74,7 +74,7 @@
                 .build()
 
         val interp = FontInterpolator()
-        assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
+        assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
     }
 
     @Test
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 fe9098f..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
@@ -967,6 +1011,92 @@
             coroutineContext.cancelChildren()
         }
 
+    @Test
+    fun `OCCLUDED to GONE`() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN keyguard goes away
+            keyguardRepository.setKeyguardShowing(false)
+            // AND occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to GONE should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.GONE)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `OCCLUDED to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
     private fun startingToWake() =
         WakefulnessModel(
             WakefulnessState.STARTING_TO_WAKE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 2a91799..746f668 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -21,7 +21,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.ScrimAlpha
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -44,21 +46,86 @@
     private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
     @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+    @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         repository = FakeKeyguardTransitionRepository()
         val interactor = KeyguardTransitionInteractor(repository)
-        underTest = PrimaryBouncerToGoneTransitionViewModel(interactor, statusBarStateController)
+        underTest =
+            PrimaryBouncerToGoneTransitionViewModel(
+                interactor,
+                statusBarStateController,
+                primaryBouncerInteractor
+            )
+
+        whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
+        whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
     }
 
     @Test
+    fun bouncerAlpha() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.6f))
+
+            assertThat(values.size).isEqualTo(3)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun bouncerAlpha_runDimissFromKeyguard() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
+
+            whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.6f))
+
+            assertThat(values.size).isEqualTo(3)
+            values.forEach { assertThat(it).isEqualTo(0f) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun scrimAlpha_runDimissFromKeyguard() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<ScrimAlpha>()
+
+            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
+
+            whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.6f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f)) }
+
+            job.cancel()
+        }
+
+    @Test
     fun scrimBehindAlpha_leaveShadeOpen() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
+            val values = mutableListOf<ScrimAlpha>()
 
-            val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
 
             whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
 
@@ -68,7 +135,9 @@
             repository.sendTransitionStep(step(1f))
 
             assertThat(values.size).isEqualTo(4)
-            values.forEach { assertThat(it).isEqualTo(1f) }
+            values.forEach {
+                assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
+            }
 
             job.cancel()
         }
@@ -76,9 +145,9 @@
     @Test
     fun scrimBehindAlpha_doNotLeaveShadeOpen() =
         runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
+            val values = mutableListOf<ScrimAlpha>()
 
-            val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+            val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
 
             whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
 
@@ -88,8 +157,10 @@
             repository.sendTransitionStep(step(1f))
 
             assertThat(values.size).isEqualTo(4)
-            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-            assertThat(values[3]).isEqualTo(0f)
+            values.forEach { assertThat(it.notificationsAlpha).isEqualTo(0f) }
+            values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
+            values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
+            assertThat(values[3].behindAlpha).isEqualTo(0f)
 
             job.cancel()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
index cc45cf88..2eca78a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -57,7 +57,18 @@
         clockView.measure(50, 50)
 
         verify(mockTextAnimator).glyphFilter = any()
-        verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, false, 350L, null, 0L, null)
+        verify(mockTextAnimator)
+            .setTextStyle(
+                weight = 300,
+                textSize = -1.0f,
+                color = 200,
+                strokeWidth = -1F,
+                animate = false,
+                duration = 350L,
+                interpolator = null,
+                delay = 0L,
+                onAnimationEnd = null
+            )
         verifyNoMoreInteractions(mockTextAnimator)
     }
 
@@ -68,8 +79,30 @@
         clockView.animateAppearOnLockscreen()
 
         verify(mockTextAnimator, times(2)).glyphFilter = any()
-        verify(mockTextAnimator).setTextStyle(100, -1.0f, 200, false, 0L, null, 0L, null)
-        verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, true, 350L, null, 0L, null)
+        verify(mockTextAnimator)
+            .setTextStyle(
+                weight = 100,
+                textSize = -1.0f,
+                color = 200,
+                strokeWidth = -1F,
+                animate = false,
+                duration = 0L,
+                interpolator = null,
+                delay = 0L,
+                onAnimationEnd = null
+            )
+        verify(mockTextAnimator)
+            .setTextStyle(
+                weight = 300,
+                textSize = -1.0f,
+                color = 200,
+                strokeWidth = -1F,
+                animate = true,
+                duration = 350L,
+                interpolator = null,
+                delay = 0L,
+                onAnimationEnd = null
+            )
         verifyNoMoreInteractions(mockTextAnimator)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2a005f2..957b0f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -403,17 +403,6 @@
     }
 
     @Test
-    public void testIsBlockingHelperShowing_isCorrectlyUpdated() throws Exception {
-        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
-
-        group.setBlockingHelperShowing(true);
-        assertTrue(group.isBlockingHelperShowing());
-
-        group.setBlockingHelperShowing(false);
-        assertFalse(group.isBlockingHelperShowing());
-    }
-
-    @Test
     public void testGetNumUniqueChildren_defaultChannel() throws Exception {
         ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index d7ac6b4..3d8a744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -117,7 +117,6 @@
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationActivityStarter mNotificationActivityStarter;
     @Mock private NotificationListContainer mNotificationListContainer;
-    @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
     @Mock private OnSettingsClickListener mOnSettingsClickListener;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private CentralSurfaces mCentralSurfaces;
@@ -173,7 +172,6 @@
 
         // Test doesn't support animation since the guts view is not attached.
         doNothing().when(guts).openControls(
-                eq(true) /* shouldDoCircularReveal */,
                 anyInt(),
                 anyInt(),
                 anyBoolean(),
@@ -190,7 +188,6 @@
         assertEquals(View.INVISIBLE, guts.getVisibility());
         mTestableLooper.processAllMessages();
         verify(guts).openControls(
-                eq(true),
                 anyInt(),
                 anyInt(),
                 anyBoolean(),
@@ -213,7 +210,6 @@
 
         // Test doesn't support animation since the guts view is not attached.
         doNothing().when(guts).openControls(
-                eq(true) /* shouldDoCircularReveal */,
                 anyInt(),
                 anyInt(),
                 anyBoolean(),
@@ -237,7 +233,6 @@
         assertTrue(mGutsManager.openGutsInternal(row, 0, 0, menuItem));
         mTestableLooper.processAllMessages();
         verify(guts).openControls(
-                eq(true),
                 anyInt(),
                 anyInt(),
                 anyBoolean(),
@@ -379,7 +374,6 @@
     public void testInitializeNotificationInfoView_PassesAlongProvisionedState() throws Exception {
         NotificationInfo notificationInfoView = mock(NotificationInfo.class);
         ExpandableNotificationRow row = spy(mHelper.createRow());
-        row.setBlockingHelperShowing(false);
         modifyRanking(row.getEntry())
                 .setUserSentiment(USER_SENTIMENT_NEGATIVE)
                 .build();
@@ -414,7 +408,6 @@
     public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
         NotificationInfo notificationInfoView = mock(NotificationInfo.class);
         ExpandableNotificationRow row = spy(mHelper.createRow());
-        row.setBlockingHelperShowing(true);
         modifyRanking(row.getEntry())
                 .setUserSentiment(USER_SENTIMENT_NEGATIVE)
                 .build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
index e696c87..fdfb4f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
@@ -76,7 +76,7 @@
     fun openControls() {
         guts.gutsContent = gutsContent
 
-        guts.openControls(true, 0, 0, false, null)
+        guts.openControls(0, 0, false, null)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index d487ba9..7a1270f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -71,7 +71,6 @@
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.policy.FakeConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -134,7 +133,6 @@
     @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock private CoroutineDispatcher mMainDispatcher;
-    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
 
     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
     //   event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@@ -249,7 +247,7 @@
 
         when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
                 .thenReturn(emptyFlow());
-        when(mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha())
+        when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha())
                 .thenReturn(emptyFlow());
 
         mScrimController = new ScrimController(
@@ -268,7 +266,6 @@
                 mStatusBarKeyguardViewManager,
                 mPrimaryBouncerToGoneTransitionViewModel,
                 mKeyguardTransitionInteractor,
-                mSysuiStatusBarStateController,
                 mMainDispatcher,
                 mLinearLargeScreenShadeInterpolator,
                 mFeatureFlags);
@@ -984,7 +981,6 @@
                 mStatusBarKeyguardViewManager,
                 mPrimaryBouncerToGoneTransitionViewModel,
                 mKeyguardTransitionInteractor,
-                mSysuiStatusBarStateController,
                 mMainDispatcher,
                 mLinearLargeScreenShadeInterpolator,
                 mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 158e9ad..e2019b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -683,4 +683,30 @@
         // the following call before registering centralSurfaces should NOT throw a NPE:
         mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
     }
+
+    @Test
+    public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
+        // GIVEN the keyguard is showing
+        reset(mAlternateBouncerInteractor);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // WHEN SBKV is reset with hideBouncerWhenShowing=true
+        mStatusBarKeyguardViewManager.reset(true);
+
+        // THEN alternate bouncer is hidden
+        verify(mAlternateBouncerInteractor).hide();
+    }
+
+    @Test
+    public void testResetHideBouncerWhenShowingIsFalse_alternateBouncerHides() {
+        // GIVEN the keyguard is showing
+        reset(mAlternateBouncerInteractor);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // WHEN SBKV is reset with hideBouncerWhenShowing=false
+        mStatusBarKeyguardViewManager.reset(false);
+
+        // THEN alternate bouncer is NOT hidden
+        verify(mAlternateBouncerInteractor, never()).hide();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index ccc57ad..ee7e082 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -18,6 +18,9 @@
 
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
 import static org.mockito.AdditionalAnswers.answerVoid;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -28,7 +31,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
@@ -38,6 +40,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -51,6 +55,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.SysuiTestCase;
@@ -90,9 +95,12 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 
@@ -398,4 +406,40 @@
         // THEN display should try wake up for the full screen intent
         verify(mCentralSurfaces).wakeUpForFullScreenIntent();
     }
+
+    @Test
+    public void testOnFullScreenIntentWhenDozing_logToStatsd() {
+        final int kTestUid = 12345;
+        final String kTestActivityName = "TestActivity";
+        // GIVEN entry that can has a full screen intent that can show
+        PendingIntent mockFullScreenIntent = mock(PendingIntent.class);
+        when(mockFullScreenIntent.getCreatorUid()).thenReturn(kTestUid);
+        ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.name = kTestActivityName;
+        when(mockFullScreenIntent.queryIntentComponents(anyInt()))
+                .thenReturn(Arrays.asList(resolveInfo));
+        Notification.Builder nb = new Notification.Builder(mContext, "a")
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setFullScreenIntent(mockFullScreenIntent, true);
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0,
+                "tag" + System.currentTimeMillis(), 0, 0,
+                nb.build(), new UserHandle(0), null, 0);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH);
+        when(entry.getSbn()).thenReturn(sbn);
+        MockitoSession mockingSession = mockitoSession()
+                .mockStatic(FrameworkStatsLog.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        // WHEN
+        mNotificationActivityStarter.launchFullScreenIntent(entry);
+
+        // THEN the full screen intent should be logged to statsd.
+        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.FULL_SCREEN_INTENT_LAUNCHED,
+                kTestUid, kTestActivityName));
+        mockingSession.finishMocking();
+    }
 }
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f913cef..bb6db9a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -199,6 +199,7 @@
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.server.ExtconStateObserver;
 import com.android.server.ExtconUEventObserver;
 import com.android.server.GestureLauncherService;
@@ -407,6 +408,7 @@
     AppOpsManager mAppOpsManager;
     PackageManager mPackageManager;
     SideFpsEventHandler mSideFpsEventHandler;
+    LockPatternUtils mLockPatternUtils;
     private boolean mHasFeatureAuto;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
@@ -1056,8 +1058,10 @@
         }
 
         synchronized (mLock) {
-            // Lock the device after the dream transition has finished.
-            mLockAfterAppTransitionFinished = true;
+            // If the setting to lock instantly on power button press is true, then set the flag to
+            // lock after the dream transition has finished.
+            mLockAfterAppTransitionFinished =
+                    mLockPatternUtils.getPowerButtonInstantlyLocks(mCurrentUserId);
         }
 
         dreamManagerInternal.requestDream();
@@ -1929,6 +1933,7 @@
         mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
         mAccessibilityShortcutController =
                 new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
+        mLockPatternUtils = new LockPatternUtils(mContext);
         mLogger = new MetricsLogger();
 
         mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b4b8cf9..43fb44c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -169,6 +169,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.StatsEventOutput;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelAllocationStats;
@@ -612,12 +613,19 @@
                         }
                     case FrameworkStatsLog.PROC_STATS:
                         synchronized (mProcStatsLock) {
-                            return pullProcStatsLocked(ProcessStats.REPORT_ALL, atomTag, data);
+                            return pullProcStatsLocked(atomTag, data);
                         }
                     case FrameworkStatsLog.PROC_STATS_PKG_PROC:
                         synchronized (mProcStatsLock) {
-                            return pullProcStatsLocked(ProcessStats.REPORT_PKG_PROC_STATS, atomTag,
-                                    data);
+                            return pullProcStatsLocked(atomTag, data);
+                        }
+                    case FrameworkStatsLog.PROCESS_STATE:
+                        synchronized (mProcStatsLock) {
+                            return pullProcessStateLocked(atomTag, data);
+                        }
+                    case FrameworkStatsLog.PROCESS_ASSOCIATION:
+                        synchronized (mProcStatsLock) {
+                            return pullProcessAssociationLocked(atomTag, data);
                         }
                     case FrameworkStatsLog.DISK_IO:
                         synchronized (mDiskIoLock) {
@@ -890,6 +898,8 @@
         registerNumFacesEnrolled();
         registerProcStats();
         registerProcStatsPkgProc();
+        registerProcessState();
+        registerProcessAssociation();
         registerDiskIO();
         registerPowerProfile();
         registerProcessCpuTime();
@@ -2870,59 +2880,138 @@
         );
     }
 
-    private int pullProcStatsLocked(int section, int atomTag, List<StatsEvent> pulledData) {
+    private void registerProcessState() {
+        int tagId = FrameworkStatsLog.PROCESS_STATE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl);
+    }
+
+    private void registerProcessAssociation() {
+        int tagId = FrameworkStatsLog.PROCESS_ASSOCIATION;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl);
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private ProcessStats getStatsFromProcessStatsService(int atomTag) {
         IProcessStats processStatsService = getIProcessStatsService();
         if (processStatsService == null) {
-            return StatsManager.PULL_SKIP;
+            return null;
         }
-
         final long token = Binder.clearCallingIdentity();
         try {
             // force procstats to flush & combine old files into one store
-            long lastHighWaterMark = readProcStatsHighWaterMark(section);
-
-            ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
-            for (int i = 0; i < protoStreams.length; i++) {
-                protoStreams[i] = new ProtoOutputStream();
-            }
-
+            long lastHighWaterMark = readProcStatsHighWaterMark(atomTag);
             ProcessStats procStats = new ProcessStats(false);
             // Force processStatsService to aggregate all in-storage and in-memory data.
-            long highWaterMark = processStatsService.getCommittedStatsMerged(
-                    lastHighWaterMark, section, true, null, procStats);
-            procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
-
-            for (int i = 0; i < protoStreams.length; i++) {
-                byte[] bytes = protoStreams[i].getBytes(); // cache the value
-                if (bytes.length > 0) {
-                    pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, bytes,
-                            // This is a shard ID, and is specified in the metric definition to be
-                            // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
-                            // keep all the shards, as it thinks each shard is a different dimension
-                            // of data.
-                            i));
-                }
-            }
-
-            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
+            long highWaterMark =
+                    processStatsService.getCommittedStatsMerged(
+                            lastHighWaterMark,
+                            ProcessStats.REPORT_ALL, // ignored since committedStats below is null.
+                            true,
+                            null, // committedStats
+                            procStats);
+            new File(
+                            mBaseDir.getAbsolutePath()
+                                    + "/"
+                                    + highWaterMarkFilePrefix(atomTag)
+                                    + "_"
+                                    + lastHighWaterMark)
                     .delete();
-            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
+            new File(
+                            mBaseDir.getAbsolutePath()
+                                    + "/"
+                                    + highWaterMarkFilePrefix(atomTag)
+                                    + "_"
+                                    + highWaterMark)
                     .createNewFile();
+            return procStats;
         } catch (RemoteException | IOException e) {
             Slog.e(TAG, "Getting procstats failed: ", e);
-            return StatsManager.PULL_SKIP;
+            return null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private int pullProcStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
+        for (int i = 0; i < protoStreams.length; i++) {
+            protoStreams[i] = new ProtoOutputStream();
+        }
+        procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
+        for (int i = 0; i < protoStreams.length; i++) {
+            byte[] bytes = protoStreams[i].getBytes(); // cache the value
+            if (bytes.length > 0) {
+                pulledData.add(
+                        FrameworkStatsLog.buildStatsEvent(
+                                atomTag,
+                                bytes,
+                                // This is a shard ID, and is specified in the metric definition to
+                                // be
+                                // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE
+                                // to
+                                // keep all the shards, as it thinks each shard is a different
+                                // dimension
+                                // of data.
+                                i));
+            }
+        }
         return StatsManager.PULL_SUCCESS;
     }
 
+    @GuardedBy("mProcStatsLock")
+    private int pullProcessStateLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        procStats.dumpProcessState(atomTag, new StatsEventOutput(pulledData));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    @GuardedBy("mProcStatsLock")
+    private int pullProcessAssociationLocked(int atomTag, List<StatsEvent> pulledData) {
+        ProcessStats procStats = getStatsFromProcessStatsService(atomTag);
+        if (procStats == null) {
+            return StatsManager.PULL_SKIP;
+        }
+        procStats.dumpProcessAssociation(atomTag, new StatsEventOutput(pulledData));
+        return StatsManager.PULL_SUCCESS;
+    }
+
+    private String highWaterMarkFilePrefix(int atomTag) {
+        // For backward compatibility, use the legacy ProcessStats enum value as the prefix for
+        // PROC_STATS and PROC_STATS_PKG_PROC.
+        if (atomTag == FrameworkStatsLog.PROC_STATS) {
+            return String.valueOf(ProcessStats.REPORT_ALL);
+        }
+        if (atomTag == FrameworkStatsLog.PROC_STATS_PKG_PROC) {
+            return String.valueOf(ProcessStats.REPORT_PKG_PROC_STATS);
+        }
+        return "atom-" + atomTag;
+    }
+
     // read high watermark for section
-    private long readProcStatsHighWaterMark(int section) {
+    private long readProcStatsHighWaterMark(int atomTag) {
         try {
-            File[] files = mBaseDir.listFiles((d, name) -> {
-                return name.toLowerCase().startsWith(String.valueOf(section) + '_');
-            });
+            File[] files =
+                    mBaseDir.listFiles(
+                            (d, name) -> {
+                                return name.toLowerCase()
+                                        .startsWith(highWaterMarkFilePrefix(atomTag) + '_');
+                            });
             if (files == null || files.length == 0) {
                 return 0;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index de5defa..681d5b6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7733,27 +7733,38 @@
 
     /**
      * Returns the requested {@link Configuration.Orientation} for the current activity.
-     *
-     * <p>When The current orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns the
-     * requested orientation for the activity below which is the first activity with an explicit
-     * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
-     * SCREEN_ORIENTATION_BEHIND}.
      */
     @Configuration.Orientation
     @Override
     int getRequestedConfigurationOrientation(boolean forDisplay) {
+        return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+    }
+
+    /**
+     * Returns the requested {@link Configuration.Orientation} for the requested
+     * {@link ActivityInfo.ScreenOrientation}.
+     *
+     * <p>When the current screen orientation is set to {@link SCREEN_ORIENTATION_BEHIND} it returns
+     * the requested orientation for the activity below which is the first activity with an explicit
+     * (different from {@link SCREEN_ORIENTATION_UNSET}) orientation which is not {@link
+     * SCREEN_ORIENTATION_BEHIND}.
+     */
+    @Configuration.Orientation
+    int getRequestedConfigurationOrientation(boolean forDisplay,
+            @ActivityInfo.ScreenOrientation int requestedOrientation) {
         if (mLetterboxUiController.hasInheritedOrientation()) {
             final RootDisplayArea root = getRootDisplayArea();
             if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
-                return ActivityInfo.reverseOrientation(
+                return reverseConfigurationOrientation(
                         mLetterboxUiController.getInheritedOrientation());
             } else {
                 return mLetterboxUiController.getInheritedOrientation();
             }
         }
-        if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
+        if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
             // We use Task here because we want to be consistent with what happens in
             // multi-window mode where other tasks orientations are ignored.
+            android.util.Log.d("orientation", "We are here");
             final ActivityRecord belowCandidate = task.getActivity(
                     a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
                     this /* boundary */, false /* includeBoundary */,
@@ -7762,7 +7773,23 @@
                 return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
             }
         }
-        return super.getRequestedConfigurationOrientation(forDisplay);
+        return super.getRequestedConfigurationOrientation(forDisplay, requestedOrientation);
+    }
+
+    /**
+     * Returns the reversed configuration orientation.
+     * @hide
+     */
+    @Configuration.Orientation
+    public static int reverseConfigurationOrientation(@Configuration.Orientation int orientation) {
+        switch (orientation) {
+            case ORIENTATION_LANDSCAPE:
+                return ORIENTATION_PORTRAIT;
+            case ORIENTATION_PORTRAIT:
+                return ORIENTATION_LANDSCAPE;
+            default:
+                return orientation;
+        }
     }
 
     /**
@@ -7808,6 +7835,19 @@
         if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
             return;
         }
+        // This is necessary in order to avoid going into size compat mode when the orientation
+        // change request comes from the app
+        if (mWmService.mLetterboxConfiguration
+                    .isSizeCompatModeDisabledAfterOrientationChangeFromApp()
+                && getRequestedConfigurationOrientation(false, requestedOrientation)
+                    != getRequestedConfigurationOrientation(false /*forDisplay */)) {
+            // Do not change the requested configuration now, because this will be done when setting
+            // the orientation below with the new mCompatDisplayInsets
+            clearSizeCompatModeAttributes();
+        }
+        ProtoLog.v(WM_DEBUG_ORIENTATION,
+                "Setting requested orientation %s for %s",
+                ActivityInfo.screenOrientationToString(requestedOrientation), this);
         setOrientation(requestedOrientation, this);
 
         // Push the new configuration to the requested app in case where it's not pushed, e.g. when
@@ -8027,17 +8067,20 @@
                         mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
     }
 
-    @VisibleForTesting
-    void clearSizeCompatMode() {
-        final float lastSizeCompatScale = mSizeCompatScale;
+    private void clearSizeCompatModeAttributes() {
         mInSizeCompatModeForBounds = false;
         mSizeCompatScale = 1f;
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
+    }
+
+    @VisibleForTesting
+    void clearSizeCompatMode() {
+        final float lastSizeCompatScale = mSizeCompatScale;
+        clearSizeCompatModeAttributes();
         if (mSizeCompatScale != lastSizeCompatScale) {
             forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
         }
-
         // Clear config override in #updateCompatDisplayInsets().
         final int activityType = getActivityType();
         final Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 491e58b..c527310 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1486,7 +1486,7 @@
         a.persistableMode = ActivityInfo.PERSIST_NEVER;
         a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
-        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_NO_HISTORY;
+        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
         a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         a.configChanges = 0xffffffff;
 
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index fa49a6ba..37cf5bc 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -19,6 +19,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
@@ -314,6 +315,10 @@
         mDeviceConfig.updateFlagActiveStatus(
                 /* isActive */ mTranslucentLetterboxingEnabled,
                 /* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY);
+        mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ true,
+                /* key */ KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+
         mLetterboxConfigurationPersister = letterboxConfigurationPersister;
         mLetterboxConfigurationPersister.start();
     }
@@ -327,6 +332,16 @@
     }
 
     /**
+     * Whether size compat mode is disabled after an orientation change request comes from the app.
+     * This value is controlled via {@link android.provider.DeviceConfig}.
+     */
+    // TODO(b/270356567) Clean up this flag
+    boolean isSizeCompatModeDisabledAfterOrientationChangeFromApp() {
+        return mDeviceConfig.getFlag(
+                KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP);
+    }
+
+    /**
      * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
      * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
      * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index df3c8f0..1651af3 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -20,7 +20,6 @@
 import android.provider.DeviceConfig;
 import android.util.ArraySet;
 
-
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Map;
@@ -53,6 +52,11 @@
 
     private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true;
 
+    static final String KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP =
+            "disable_size_compat_mode_after_orientation_change_from_app";
+    private static final boolean
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP = true;
+
     @VisibleForTesting
     static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
             KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
@@ -64,7 +68,9 @@
             KEY_ENABLE_COMPAT_FAKE_FOCUS,
             DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS,
             KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
-            DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY
+            DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY,
+            KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP,
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP
     );
 
     // Whether camera compatibility treatment is enabled.
@@ -93,6 +99,10 @@
     private boolean mIsTranslucentLetterboxingAllowed =
             DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY;
 
+    // Whether size compat mode is disabled after an orientation change request comes from the app
+    private boolean mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+            DEFAULT_VALUE_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP;
+
     // Set of active device configs that need to be updated in
     // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
     private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -142,6 +152,8 @@
                 return mIsCompatFakeFocusAllowed;
             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
                 return mIsTranslucentLetterboxingAllowed;
+            case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+                return mIsSizeCompatModeDisabledAfterOrientationChangeFromApp;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
@@ -169,6 +181,10 @@
             case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY:
                 mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue);
                 break;
+            case KEY_DISABLE_SIZE_COMPAT_MODE_AFTER_ORIENTATION_CHANGE_FROM_APP:
+                mIsSizeCompatModeDisabledAfterOrientationChangeFromApp =
+                        getDeviceConfig(key, defaultValue);
+                break;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9e0e8c4..7ff92af 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1456,7 +1456,26 @@
      */
     @Configuration.Orientation
     int getRequestedConfigurationOrientation(boolean forDisplay) {
-        int requestedOrientation = getOverrideOrientation();
+        return getRequestedConfigurationOrientation(forDisplay, getOverrideOrientation());
+    }
+
+    /**
+     * Gets the configuration orientation by the requested screen orientation
+     *
+     * @param forDisplay whether it is the requested config orientation for display.
+     *                   If {@code true}, we may reverse the requested orientation if the root is
+     *                   different from the display, so that when the display rotates to the
+     *                   reversed orientation, the requested app will be in the requested
+     *                   orientation.
+     * @param requestedOrientation the screen orientation({@link ScreenOrientation}) that is
+     *                   requested
+     * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+     *         {@link Configuration#ORIENTATION_PORTRAIT},
+     *         {@link Configuration#ORIENTATION_UNDEFINED}).
+     */
+    @Configuration.Orientation
+    int getRequestedConfigurationOrientation(boolean forDisplay,
+            @ScreenOrientation int requestedOrientation) {
         final RootDisplayArea root = getRootDisplayArea();
         if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
             // Reverse the requested orientation if the orientation of its root is different from
@@ -1466,7 +1485,7 @@
             // (portrait).
             // When an app below the DAG is requesting landscape, it should actually request the
             // display to be portrait, so that the DAG and the app will be in landscape.
-            requestedOrientation = reverseOrientation(getOverrideOrientation());
+            requestedOrientation = reverseOrientation(requestedOrientation);
         }
 
         if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
index 0993295..2505abf2 100644
--- a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
@@ -152,6 +152,8 @@
             }
         } catch (SQLiteException exception) {
             Slog.w("SQLite exception when querying contacts.", exception);
+        } catch (IllegalArgumentException exception) {
+            Slog.w("Illegal Argument exception when querying contacts.", exception);
         }
         if (found && lookupKey != null && hasPhoneNumber) {
             return queryPhoneNumber(lookupKey);
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/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
index 299f153..16a02b6 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
@@ -91,8 +91,16 @@
     }
 
     @Test
-    public void testQueryException_returnsFalse() {
-        contentProvider.setThrowException(true);
+    public void testQuerySQLiteException_returnsFalse() {
+        contentProvider.setThrowSQLiteException(true);
+
+        Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+        assertFalse(mHelper.query(contactUri.toString()));
+    }
+
+    @Test
+    public void testQueryIllegalArgumentException_returnsFalse() {
+        contentProvider.setThrowIllegalArgumentException(true);
 
         Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
         assertFalse(mHelper.query(contactUri.toString()));
@@ -178,14 +186,18 @@
     private class ContactsContentProvider extends MockContentProvider {
 
         private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>();
-        private boolean throwException = false;
+        private boolean mThrowSQLiteException = false;
+        private boolean mThrowIllegalArgumentException = false;
 
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                 String sortOrder) {
-            if (throwException) {
+            if (mThrowSQLiteException) {
                 throw new SQLiteException();
             }
+            if (mThrowIllegalArgumentException) {
+                throw new IllegalArgumentException();
+            }
 
             for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) {
                 if (uri.isPathPrefixMatch(prefixUri)) {
@@ -195,8 +207,12 @@
             return mUriPrefixToCursorMap.get(uri);
         }
 
-        public void setThrowException(boolean throwException) {
-            this.throwException = throwException;
+        public void setThrowSQLiteException(boolean throwException) {
+            this.mThrowSQLiteException = throwException;
+        }
+
+        public void setThrowIllegalArgumentException(boolean throwException) {
+            this.mThrowIllegalArgumentException = throwException;
         }
 
         private void registerCursor(Uri uriPrefix, Cursor cursor) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ea50179..0300eb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2377,7 +2377,7 @@
                 .setScreenOrientation(SCREEN_ORIENTATION_BEHIND)
                 .build();
         final int topOrientation = activityTop.getRequestedConfigurationOrientation();
-        assertEquals(SCREEN_ORIENTATION_PORTRAIT, topOrientation);
+        assertEquals(ORIENTATION_PORTRAIT, topOrientation);
     }
 
     private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 117805b..d77b6ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -381,7 +381,7 @@
     }
 
     @Test
-    public void testTranslucentActivitiesDontGoInSizeCompactMode() {
+    public void testTranslucentActivitiesDontGoInSizeCompatMode() {
         mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2454,11 +2454,11 @@
         assertFalse(mActivity.inSizeCompatMode());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
-
-        assertTrue(mActivity.inSizeCompatMode());
-        // We should remember the original orientation.
+        // Activity is not in size compat mode because the orientation change request came from the
+        // app itself
+        assertFalse(mActivity.inSizeCompatMode());
         assertEquals(mActivity.getResolvedOverrideConfiguration().orientation,
-                Configuration.ORIENTATION_PORTRAIT);
+                Configuration.ORIENTATION_UNDEFINED);
     }
 
     @Test
@@ -3033,6 +3033,25 @@
     }
 
     @Test
+    public void testAppRequestsOrientationChange_notInSizeCompat() {
+        setUpDisplaySizeWithApp(2200, 1800);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        // Activity is not in size compat mode because the orientation change request came from the
+        // app itself
+        assertFalse(mActivity.inSizeCompatMode());
+
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
+        // Activity should go into size compat mode now because the orientation change came from the
+        // system (device rotation)
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
+    @Test
     public void testLetterboxDetailsForStatusBar_noLetterbox() {
         setUpDisplaySizeWithApp(2800, 1000);
         addStatusBar(mActivity.mDisplayContent);