Merge "Bluetooth : Correct the Network reset handling" into r-keystone-qcom-dev
diff --git a/Android.bp b/Android.bp
index 632f49d..7c2d6eb 100755
--- a/Android.bp
+++ b/Android.bp
@@ -264,6 +264,7 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
+        ":resourcemanager_aidl",
         ":storaged_aidl",
         ":vold_aidl",
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9dcba7c..5987a72 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -951,6 +951,7 @@
                                                    const int64_t& nextBucketStartTimeNs) {
     if (mCondition == ConditionState::kUnknown) {
         StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+        invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
     }
 
     VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 13d977f..1e6680c 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -13,6 +13,11 @@
 // limitations under the License.
 
 #include "StatsLogProcessor.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
 #include "StatsService.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -20,16 +25,10 @@
 #include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 #include "packages/UidMap.h"
-#include "storage/StorageManager.h"
 #include "statslog_statsdtest.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
+#include "storage/StorageManager.h"
 #include "tests/statsd_test_util.h"
 
-#include <stdio.h>
-
 using namespace android;
 using namespace testing;
 using ::ndk::SharedRefBase;
@@ -1836,6 +1835,11 @@
     int isolatedUid = 30;
     sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
     ConfigKey key(3, 4);
+
+    // TODO: All tests should not persist state on disk. This removes any reports that were present.
+    ProtoOutputStream proto;
+    StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false);
+
     StatsdConfig config = MakeConfig(false);
     sp<StatsLogProcessor> processor =
             CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index f041996..5666501 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -3295,11 +3295,15 @@
               report.value_metrics().skipped(0).start_bucket_elapsed_millis());
     EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
               report.value_metrics().skipped(0).end_bucket_elapsed_millis());
-    ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+    ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
     EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
     EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
+
+    dropEvent = report.value_metrics().skipped(0).drop_event(1);
+    EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis());
 }
 
 /*
@@ -3615,7 +3619,7 @@
 
     sp<ValueMetricProducer> valueProducer =
             ValueMetricProducerTestHelper::createValueProducerWithCondition(
-                    pullerManager, metric, ConditionState::kUnknown);
+                    pullerManager, metric, ConditionState::kFalse);
 
     // Check dump report.
     ProtoOutputStream output;
@@ -3641,6 +3645,94 @@
 }
 
 /*
+ * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+            // Condition change to true.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10));
+                return true;
+            }))
+            // Dump report requested.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(
+                    pullerManager, metric, ConditionState::kUnknown);
+
+    // Bucket should be dropped because of condition unknown.
+    int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC;
+    valueProducer->notifyAppUpgrade(appUpgradeTimeNs);
+
+    // Bucket also dropped due to condition unknown
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    // This bucket is also dropped due to condition unknown.
+    int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC;
+    valueProducer->onConditionChanged(true, conditionChangeTimeNs);
+
+    // Check dump report.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds
+    valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
+                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(0, report.value_metrics().data_size());
+    ASSERT_EQ(3, report.value_metrics().skipped_size());
+
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+              report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+              report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+    auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+    EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
+
+    EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+              report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+              report.value_metrics().skipped(1).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
+
+    dropEvent = report.value_metrics().skipped(1).drop_event(0);
+    EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
+
+    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+              report.value_metrics().skipped(2).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+              report.value_metrics().skipped(2).end_bucket_elapsed_millis());
+    ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size());
+
+    dropEvent = report.value_metrics().skipped(2).drop_event(0);
+    EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
  * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
  * was not flushed in time.
  */
@@ -4957,7 +5049,7 @@
     ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
 
     auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
-    EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+    EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
     EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
 }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ddc5747..e620f16 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -261,8 +261,9 @@
                             < SystemClock.elapsedRealtime()) {
                         String stackTrace = getFormattedStackTrace();
                         try {
+                            String packageName = ActivityThread.currentOpPackageName();
                             sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
-                                    ActivityThread.currentOpPackageName(), op, stackTrace);
+                                    packageName == null ? "" : packageName, op, stackTrace);
                         } catch (RemoteException e) {
                             e.rethrowFromSystemServer();
                         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c8dd4d9..885ffba 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -205,6 +205,7 @@
     public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
     public static final String TAG_USES_SDK = "uses-sdk";
     public static final String TAG_USES_SPLIT = "uses-split";
+    public static final String TAG_PROFILEABLE = "profileable";
 
     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -459,6 +460,9 @@
         public final SigningDetails signingDetails;
         public final boolean coreApp;
         public final boolean debuggable;
+        // This does not represent the actual manifest structure since the 'profilable' tag
+        // could be used with attributes other than 'shell'. Extend if necessary.
+        public final boolean profilableByShell;
         public final boolean multiArch;
         public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
@@ -470,15 +474,13 @@
         public final int overlayPriority;
 
         public ApkLite(String codePath, String packageName, String splitName,
-                boolean isFeatureSplit,
-                String configForSplit, String usesSplitName, boolean isSplitRequired,
-                int versionCode, int versionCodeMajor,
-                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                SigningDetails signingDetails, boolean coreApp,
-                boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
-                String targetPackageName, boolean overlayIsStatic, int overlayPriority,
-                int minSdkVersion, int targetSdkVersion) {
+                boolean isFeatureSplit, String configForSplit, String usesSplitName,
+                boolean isSplitRequired, int versionCode, int versionCodeMajor, int revisionCode,
+                int installLocation, List<VerifierInfo> verifiers, SigningDetails signingDetails,
+                boolean coreApp, boolean debuggable, boolean profilableByShell, boolean multiArch,
+                boolean use32bitAbi, boolean useEmbeddedDex, boolean extractNativeLibs,
+                boolean isolatedSplits, String targetPackageName, boolean overlayIsStatic,
+                int overlayPriority, int minSdkVersion, int targetSdkVersion) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -493,6 +495,7 @@
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
             this.coreApp = coreApp;
             this.debuggable = debuggable;
+            this.profilableByShell = profilableByShell;
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
             this.useEmbeddedDex = useEmbeddedDex;
@@ -1573,6 +1576,7 @@
         int revisionCode = 0;
         boolean coreApp = false;
         boolean debuggable = false;
+        boolean profilableByShell = false;
         boolean multiArch = false;
         boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
@@ -1638,6 +1642,10 @@
                     final String attr = attrs.getAttributeName(i);
                     if ("debuggable".equals(attr)) {
                         debuggable = attrs.getAttributeBooleanValue(i, false);
+                        if (debuggable) {
+                            // Debuggable implies profileable
+                            profilableByShell = true;
+                        }
                     }
                     if ("multiArch".equals(attr)) {
                         multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -1690,6 +1698,13 @@
                         minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
                     }
                 }
+            } else if (TAG_PROFILEABLE.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("shell".equals(attr)) {
+                        profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+                    }
+                }
             }
         }
 
@@ -1707,8 +1722,9 @@
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                 configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                 revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
-                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
-                targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
+                profilableByShell, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
+                isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
+                targetSdkVersion);
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index d2172d3..c3e9402 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -20,7 +20,6 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
-import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -303,6 +302,7 @@
         int revisionCode = 0;
         boolean coreApp = false;
         boolean debuggable = false;
+        boolean profilableByShell = false;
         boolean multiArch = false;
         boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
@@ -379,6 +379,10 @@
                     switch (attr) {
                         case "debuggable":
                             debuggable = attrs.getAttributeBooleanValue(i, false);
+                            if (debuggable) {
+                                // Debuggable implies profileable
+                                profilableByShell = true;
+                            }
                             break;
                         case "multiArch":
                             multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -431,6 +435,13 @@
                         minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
                     }
                 }
+            } else if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("shell".equals(attr)) {
+                        profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+                    }
+                }
             }
         }
 
@@ -445,12 +456,13 @@
             overlayPriority = 0;
         }
 
-        return input.success(new PackageParser.ApkLite(codePath, packageSplit.first,
-                packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, isSplitRequired,
-                versionCode, versionCodeMajor, revisionCode, installLocation, verifiers,
-                signingDetails, coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex,
-                extractNativeLibs, isolatedSplits, targetPackage, overlayIsStatic, overlayPriority,
-                minSdkVersion, targetSdkVersion));
+        return input.success(
+                new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
+                        isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+                        versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
+                        coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
+                        useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
+                        overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion));
     }
 
     public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 220ce22..61e6a05 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -111,6 +111,11 @@
     void deleteStorage(int storageId);
 
     /**
+     * Permanently disable readlogs reporting for a storage given its ID.
+     */
+    void disableReadLogs(int storageId);
+
+    /**
      * Setting up native library directories and extract native libs onto a storage if needed.
      */
     boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi, boolean extractNativeLibs);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 863d86ef..31ccf95 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -153,6 +153,13 @@
     }
 
     /**
+     * Permanently disables readlogs.
+     */
+    public void disableReadLogs() {
+        mDefaultStorage.disableReadLogs();
+    }
+
+    /**
      * Resets the states and unbinds storage instances for an installation session.
      * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
      */
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 6200a38..ca6114f 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -418,6 +418,17 @@
     private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
 
     /**
+     * Permanently disable readlogs collection.
+     */
+    public void disableReadLogs() {
+        try {
+            mService.disableReadLogs(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Deserialize and validate v4 signature bytes.
      */
     private static void validateV4Signature(@Nullable byte[] v4signatureBytes)
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index ef21900..4adcd69 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -219,6 +219,9 @@
     /**
      * Listen for changes to observed cell info.
      *
+     * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * permission.
+     *
      * @see #onCellInfoChanged
      */
     public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -461,6 +464,9 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
      * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
      * @see #onRegistrationFailed
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -472,6 +478,9 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
      * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
+     * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+     * of whether the calling app has carrier privileges.
+     *
      * @see #onBarringInfoChanged
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -569,6 +578,11 @@
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
+     * The instance of {@link ServiceState} passed as an argument here will have various levels of
+     * location information stripped from it depending on the location permissions that your app
+     * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+     * receive all the information in {@link ServiceState}.
+     *
      * @see ServiceState#STATE_EMERGENCY_ONLY
      * @see ServiceState#STATE_IN_SERVICE
      * @see ServiceState#STATE_OUT_OF_SERVICE
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 397b04e..17620fa 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -357,6 +357,19 @@
         return mSources.get(type);
     }
 
+    /**
+     * Returns the source visibility or the default visibility if the source doesn't exist. This is
+     * useful if when treating this object as a request.
+     *
+     * @param type The {@link InternalInsetsType} to query.
+     * @return {@code true} if the source is visible or the type is default visible and the source
+     *         doesn't exist.
+     */
+    public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
+        final InsetsSource source = mSources.get(type);
+        return source != null ? source.isVisible() : getDefaultVisibility(type);
+    }
+
     public void setDisplayFrame(Rect frame) {
         mDisplayFrame.set(frame);
     }
@@ -388,20 +401,6 @@
         }
     }
 
-    /**
-     * A shortcut for setting the visibility of the source.
-     *
-     * @param type The {@link InternalInsetsType} of the source to set the visibility
-     * @param referenceState The {@link InsetsState} for reference
-     */
-    public void setSourceVisible(@InternalInsetsType int type, InsetsState referenceState) {
-        InsetsSource source = mSources.get(type);
-        InsetsSource referenceSource = referenceState.mSources.get(type);
-        if (source != null && referenceSource != null) {
-            source.setVisible(referenceSource.isVisible());
-        }
-    }
-
     public void set(InsetsState other) {
         set(other, false /* copySources */);
     }
@@ -490,7 +489,7 @@
         }
     }
 
-    public static boolean getDefaultVisibility(@InsetsType int type) {
+    public static boolean getDefaultVisibility(@InternalInsetsType int type) {
         return type != ITYPE_IME;
     }
 
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index d63ebda..fc8cafd 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -427,6 +427,8 @@
                 && dri.getResolveInfo().targetUserId == UserHandle.USER_CURRENT) {
             if (shouldAddResolveInfo(dri)) {
                 mDisplayList.add(dri);
+                Log.i(TAG, "Add DisplayResolveInfo component: " + dri.getResolvedComponentName()
+                        + ", intent component: " + dri.getResolvedIntent().getComponent());
             }
         }
     }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 21ca948..d9ca9c2 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1197,6 +1197,10 @@
             addFeature(PackageManager.FEATURE_APP_ENUMERATION, 0);
         }
 
+        if (Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.Q) {
+            addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
+        }
+
         for (String featureName : mUnavailableFeatures) {
             removeFeature(featureName);
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 80577ce..e692b04 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3571,6 +3571,8 @@
          @hide -->
     <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
          android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
+
 
     <!-- Must be required by a {@link android.media.routing.MediaRouteService}
          to ensure that only the system can interact with it.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0f30544..40d47a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4365,9 +4365,6 @@
     <!-- The delete-widget drop target button text -->
     <string name="kg_reordering_delete_drop_target_text">Remove</string>
 
-    <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
-    <string name="allow_while_in_use_permission_in_fgs">The background started foreground service from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bugreport.</string>
-
     <!-- Message shown in dialog when user is attempting to set the music volume above the
     recommended maximum level for headphones -->
     <string name="safe_media_volume_warning" product="default">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ae3b82e..4b3d5168 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4034,9 +4034,6 @@
   <java-symbol type="dimen" name="resolver_empty_state_container_padding_top" />
   <java-symbol type="dimen" name="resolver_empty_state_container_padding_bottom" />
 
-  <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
-  <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
-
   <java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" />
 
   <!-- Whether to expand the lock screen user switcher by default -->
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 308c1a5..4f4364f 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -13,26 +13,26 @@
 // limitations under the License.
 
 android_library_import {
-    name: "window-extensions",
-    aars: ["window-extensions-release.aar"],
+    name: "window-sidecar",
+    aars: ["window-sidecar-release.aar"],
     sdk_version: "current",
 }
 
 java_library {
-    name: "androidx.window.extensions",
+    name: "androidx.window.sidecar",
     srcs: ["src/**/*.java"],
-    static_libs: ["window-extensions"],
+    static_libs: ["window-sidecar"],
     installable: true,
     sdk_version: "core_platform",
     vendor: true,
     libs: ["framework", "androidx.annotation_annotation",],
-    required: ["androidx.window.extensions.xml",],
+    required: ["androidx.window.sidecar.xml",],
 }
 
 prebuilt_etc {
-    name: "androidx.window.extensions.xml",
+    name: "androidx.window.sidecar.xml",
     vendor: true,
     sub_dir: "permissions",
-    src: "androidx.window.extensions.xml",
+    src: "androidx.window.sidecar.xml",
     filename_from_src: true,
 }
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
deleted file mode 100644
index 1f0ff66..0000000
--- a/libs/WindowManager/Jetpack/androidx.window.extensions.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright 2020 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.
-  -->
-<permissions>
-    <library
-        name="androidx.window.extensions"
-        file="/vendor/framework/androidx.window.extensions.jar"/>
-</permissions>
diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
new file mode 100644
index 0000000..f88a5f4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<permissions>
+    <library
+        name="androidx.window.sidecar"
+        file="/vendor/framework/androidx.window.sidecar.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
similarity index 79%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
index 7a3fbf3..92e5758 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions;
+package androidx.window.sidecar;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static androidx.window.extensions.ExtensionHelper.getWindowDisplay;
-import static androidx.window.extensions.ExtensionHelper.isInMultiWindow;
-import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation;
-import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect;
+import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
+import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
+import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
+import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -42,8 +42,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-class SettingsExtensionImpl extends StubExtension {
-    private static final String TAG = "SettingsExtension";
+class SettingsSidecarImpl extends StubSidecar {
+    private static final String TAG = "SettingsSidecar";
 
     private static final String DEVICE_POSTURE = "device_posture";
     private static final String DISPLAY_FEATURES = "display_features";
@@ -106,7 +106,7 @@
         }
     }
 
-    SettingsExtensionImpl(Context context) {
+    SettingsSidecarImpl(Context context) {
         mContext = context;
         mSettingsObserver = new SettingsObserver();
     }
@@ -118,29 +118,33 @@
     /** Update display features with values read from settings. */
     private void updateDisplayFeatures() {
         for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
-            ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
             updateWindowLayout(windowToken, newLayout);
         }
     }
 
     @NonNull
     @Override
-    public ExtensionDeviceState getDeviceState() {
+    public SidecarDeviceState getDeviceState() {
         ContentResolver resolver = mContext.getContentResolver();
         int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
-                ExtensionDeviceState.POSTURE_UNKNOWN);
-        return new ExtensionDeviceState(posture);
+                SidecarDeviceState.POSTURE_UNKNOWN);
+        SidecarDeviceState deviceState = new SidecarDeviceState();
+        deviceState.posture = posture;
+        return deviceState;
     }
 
     @NonNull
     @Override
-    public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
-        List<ExtensionDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
-        return new ExtensionWindowLayoutInfo(displayFeatures);
+    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+        List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
+        SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+        windowLayoutInfo.displayFeatures = displayFeatures;
+        return windowLayoutInfo;
     }
 
-    private List<ExtensionDisplayFeature> readDisplayFeatures(IBinder windowToken) {
-        List<ExtensionDisplayFeature> features = new ArrayList<ExtensionDisplayFeature>();
+    private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
+        List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
         int displayId = getWindowDisplay(windowToken);
         if (displayId != DEFAULT_DISPLAY) {
             Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -170,10 +174,10 @@
                 int type;
                 switch (featureType) {
                     case FEATURE_TYPE_FOLD:
-                        type = ExtensionDisplayFeature.TYPE_FOLD;
+                        type = SidecarDisplayFeature.TYPE_FOLD;
                         break;
                     case FEATURE_TYPE_HINGE:
-                        type = ExtensionDisplayFeature.TYPE_HINGE;
+                        type = SidecarDisplayFeature.TYPE_HINGE;
                         break;
                     default: {
                         Log.e(TAG, "Malformed feature type: " + featureType);
@@ -189,8 +193,9 @@
                 rotateRectToDisplayRotation(featureRect, displayId);
                 transformToWindowSpaceRect(featureRect, windowToken);
                 if (!featureRect.isEmpty()) {
-                    ExtensionDisplayFeature feature =
-                            new ExtensionDisplayFeature(featureRect, type);
+                    SidecarDisplayFeature feature = new SidecarDisplayFeature();
+                    feature.setRect(featureRect);
+                    feature.setType(type);
                     features.add(feature);
                 } else {
                     Log.w(TAG, "Failed to adjust feature to window");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
similarity index 92%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
index c61f1ed..e5b6cff 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions;
+package androidx.window.sidecar;
 
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_0;
@@ -32,12 +32,7 @@
 
 import androidx.annotation.Nullable;
 
-/**
- * Toolkit class for calculation of the display feature bounds within the window.
- * NOTE: This sample implementation only works for Activity windows, because there is no public APIs
- * to obtain layout params or bounds for arbitrary windows.
- */
-class ExtensionHelper {
+class SidecarHelper {
     /**
      * Rotate the input rectangle specified in default display orientation to the current display
      * rotation.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
similarity index 70%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 47349f1..0b4915e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions;
+package androidx.window.sidecar;
 
 import android.content.Context;
 
@@ -22,14 +22,13 @@
  * Provider class that will instantiate the library implementation. It must be included in the
  * vendor library, and the vendor implementation must match the signature of this class.
  */
-public class ExtensionProvider {
-
+public class SidecarProvider {
     /**
-     * The support library will instantiate the vendor implementation using this interface.
-     * @return An implementation of {@link ExtensionInterface}.
+     * Provide a simple implementation of {@link SidecarInterface} that can be replaced by
+     * an OEM by overriding this method.
      */
-    public static ExtensionInterface getExtensionImpl(Context context) {
-        return new SettingsExtensionImpl(context);
+    public static SidecarInterface getSidecarImpl(Context context) {
+        return new SettingsSidecarImpl(context);
     }
 
     /**
@@ -37,6 +36,6 @@
      * @return API version string in MAJOR.MINOR.PATCH-description format.
      */
     public static String getApiVersion() {
-        return "1.0.0-settings_sample";
+        return "0.1.0-settings_sample";
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
new file mode 100644
index 0000000..199c373
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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 androidx.window.sidecar;
+
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubSidecar implements SidecarInterface {
+
+    private SidecarCallback mSidecarCallback;
+    private final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
+    private boolean mDeviceStateChangeListenerRegistered;
+
+    StubSidecar() {
+    }
+
+    @Override
+    public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) {
+        this.mSidecarCallback = sidecarCallback;
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) {
+        this.mWindowLayoutChangeListenerTokens.add(iBinder);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) {
+        this.mWindowLayoutChangeListenerTokens.remove(iBinder);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onDeviceStateListenersChanged(boolean isEmpty) {
+        this.mDeviceStateChangeListenerRegistered = !isEmpty;
+        this.onListenersChanged();
+    }
+
+    void updateDeviceState(SidecarDeviceState newState) {
+        if (this.mSidecarCallback != null) {
+            mSidecarCallback.onDeviceStateChanged(newState);
+        }
+    }
+
+    void updateWindowLayout(@NonNull IBinder windowToken,
+            @NonNull SidecarWindowLayoutInfo newLayout) {
+        if (this.mSidecarCallback != null) {
+            mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout);
+        }
+    }
+
+    @NonNull
+    Set<IBinder> getWindowsListeningForLayoutChanges() {
+        return mWindowLayoutChangeListenerTokens;
+    }
+
+    protected boolean hasListeners() {
+        return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered;
+    }
+
+    protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
deleted file mode 100644
index 0ebbb86..0000000
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-sidecar-release.aar b/libs/WindowManager/Jetpack/window-sidecar-release.aar
new file mode 100644
index 0000000..50f101d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-sidecar-release.aar
Binary files differ
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index a18cfccb..5a7c87e0 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -576,6 +576,10 @@
     }
 
     void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
+        if (preferredFeatures == null) {
+            mPreferredFeaturesMap.remove(packageName);
+            return;
+        }
         List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
         if ((prevFeatures == null && preferredFeatures.size() == 0)
                 || Objects.equals(preferredFeatures, prevFeatures)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 4c720ab..37dfce4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -16,10 +16,10 @@
 
 package com.android.systemui.car.navigationbar;
 
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
 import static android.view.InsetsState.containsType;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
@@ -368,15 +368,13 @@
             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT,
                     height,
-                    WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+                    WindowManager.LayoutParams.TYPE_STATUS_BAR,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                             | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                     PixelFormat.TRANSLUCENT);
             lp.setTitle("TopCarNavigationBar");
-            lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
-            lp.setFitInsetsTypes(0);
             lp.windowAnimations = 0;
             lp.gravity = Gravity.TOP;
             mWindowManager.addView(mTopNavigationBarWindow, lp);
@@ -390,14 +388,13 @@
             WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT,
                     height,
-                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                             | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                     PixelFormat.TRANSLUCENT);
             lp.setTitle("BottomCarNavigationBar");
-            lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES};
             lp.windowAnimations = 0;
             lp.gravity = Gravity.BOTTOM;
             mWindowManager.addView(mBottomNavigationBarWindow, lp);
@@ -415,6 +412,8 @@
                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                     PixelFormat.TRANSLUCENT);
             leftlp.setTitle("LeftCarNavigationBar");
+            leftlp.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
+            leftlp.setFitInsetsTypes(0);
             leftlp.windowAnimations = 0;
             leftlp.gravity = Gravity.LEFT;
             mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
@@ -432,6 +431,8 @@
                             | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                     PixelFormat.TRANSLUCENT);
             rightlp.setTitle("RightCarNavigationBar");
+            rightlp.providesInsetsTypes = new int[]{ITYPE_EXTRA_NAVIGATION_BAR};
+            rightlp.setFitInsetsTypes(0);
             rightlp.windowAnimations = 0;
             rightlp.gravity = Gravity.RIGHT;
             mWindowManager.addView(mRightNavigationBarWindow, rightlp);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 0ced402..029d4c7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,10 +16,7 @@
 
 package com.android.systemui.car.navigationbar;
 
-import static android.view.WindowInsets.Type.systemBars;
-
 import android.content.Context;
-import android.graphics.Insets;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -82,28 +79,9 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
-        applyMargins(windowInsets.getInsets(systemBars()));
         return windowInsets;
     }
 
-    private void applyMargins(Insets insets) {
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            if (child.getLayoutParams() instanceof LayoutParams) {
-                LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
-                        || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
-                    lp.rightMargin = insets.right;
-                    lp.leftMargin = insets.left;
-                    lp.topMargin = insets.top;
-                    lp.bottomMargin = insets.bottom;
-                    child.requestLayout();
-                }
-            }
-        }
-    }
-
     // Used to forward touch events even if the touch was initiated from a child component
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3bf43dd..3565b0e 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -30,9 +30,7 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.DrawableWrapper;
 import android.os.RemoteException;
-import android.util.DisplayMetrics;
 import android.util.PathParser;
-import android.view.Display;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -47,6 +45,9 @@
  */
 public class AdaptiveOutlineDrawable extends DrawableWrapper {
 
+    private static final float ADVANCED_ICON_CENTER = 50f;
+    private static final float ADVANCED_ICON_RADIUS = 48f;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
     public @interface AdaptiveOutlineIconType {
@@ -61,7 +62,6 @@
     private int mStrokeWidth;
     private Bitmap mBitmap;
     private int mType;
-    private float mDensity;
 
     public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
         super(new AdaptiveIconShapeDrawable(resources));
@@ -83,7 +83,6 @@
         mPath = new Path(PathParser.createPathFromPathData(
                 resources.getString(com.android.internal.R.string.config_icon_mask)));
         mStrokeWidth = resources.getDimensionPixelSize(R.dimen.adaptive_outline_stroke);
-        mDensity = resources.getDisplayMetrics().density;
         mOutlinePaint = new Paint();
         mOutlinePaint.setColor(getColor(resources, type));
         mOutlinePaint.setStyle(Paint.Style.STROKE);
@@ -137,12 +136,7 @@
         if (mType == TYPE_DEFAULT) {
             canvas.drawPath(mPath, mOutlinePaint);
         } else {
-            final float defaultDensity = getDefaultDisplayDensity(Display.DEFAULT_DISPLAY)
-                    / (float) DisplayMetrics.DENSITY_DEFAULT;
-            final int insetPx =
-                    Math.round(mInsetPx / (float) (Math.floor(
-                            (mDensity / defaultDensity) * 100) / 100.0));
-            canvas.drawCircle(2 * insetPx, 2 * insetPx, 2 * insetPx - mStrokeWidth,
+            canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
                     mOutlinePaint);
         }
         canvas.restoreToCount(count);
diff --git a/packages/SettingsLib/SearchWidget/res/values-el/strings.xml b/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
index 6f5ab78..d50436a 100644
--- a/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Ρυθμίσεις αναζήτησης"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Αναζήτηση στις ρυθμίσεις"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml b/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
index 8fa5a84..b68b792 100644
--- a/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Որոնման կարգավորումներ"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Որոնեք կարգավորումներ"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 85a8d73..5fe116e 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Pesquisa de definições"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Pesquisar nas definições"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml b/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
index a531321..354941d 100644
--- a/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Cilësimet e kërkimit"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Kërko te cilësimet"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml b/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
index dfd66b2..560ac13 100644
--- a/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Налаштування пошуку"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Пошук налаштувань"</string>
 </resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml b/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
index cb1a75a..90daf11 100644
--- a/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Tìm kiếm trong các mục cài đặt"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Tìm trong thông tin cài đặt"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 137116f..376c2e7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -239,7 +239,7 @@
     <string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
     <string name="keep_screen_on_summary" msgid="1510731514101925829">"Pantaila ez da ezarriko inoiz inaktibo kargatu bitartean"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Gaitu Bluetooth HCI miatze-erregistroa"</string>
-    <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetooth konexioa ezarpena aldatu ostean)."</string>
+    <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetooth-a ezarpena aldatu ostean)."</string>
     <string name="oem_unlock_enable" msgid="5334869171871566731">"OEM desblokeoa"</string>
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Onartu abiarazlea desblokeatzea"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM desblokeoa onartu nahi duzu?"</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index de38625..3018683 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -22,13 +22,13 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wifi_status">
     <item msgid="1596683495752107015"></item>
-    <item msgid="3288373008277313483">"Scansione in corso..."</item>
-    <item msgid="6050951078202663628">"Connessione..."</item>
-    <item msgid="8356618438494652335">"Autenticazione..."</item>
-    <item msgid="2837871868181677206">"Acquisizione indirizzo IP..."</item>
+    <item msgid="3288373008277313483">"Scansione in corso…"</item>
+    <item msgid="6050951078202663628">"Connessione…"</item>
+    <item msgid="8356618438494652335">"Autenticazione…"</item>
+    <item msgid="2837871868181677206">"Acquisizione indirizzo IP…"</item>
     <item msgid="4613015005934755724">"Connessa"</item>
     <item msgid="3763530049995655072">"Sospesa"</item>
-    <item msgid="7852381437933824454">"Disconnessione..."</item>
+    <item msgid="7852381437933824454">"Disconnessione…"</item>
     <item msgid="5046795712175415059">"Disconnessa"</item>
     <item msgid="2473654476624070462">"Operazione non riuscita"</item>
     <item msgid="9146847076036105115">"Bloccato"</item>
@@ -36,13 +36,13 @@
   </string-array>
   <string-array name="wifi_status_with_ssid">
     <item msgid="5969842512724979061"></item>
-    <item msgid="1818677602615822316">"Scansione in corso..."</item>
-    <item msgid="8339720953594087771">"Connessione a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
-    <item msgid="3028983857109369308">"Autenticazione con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
-    <item msgid="4287401332778341890">"Acquisizione indirizzo IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+    <item msgid="1818677602615822316">"Scansione in corso…"</item>
+    <item msgid="8339720953594087771">"Connessione a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+    <item msgid="3028983857109369308">"Autenticazione con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+    <item msgid="4287401332778341890">"Acquisizione indirizzo IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="1043944043827424501">"Connessa a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
     <item msgid="7445993821842009653">"Sospesa"</item>
-    <item msgid="1175040558087735707">"Disconnessione da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+    <item msgid="1175040558087735707">"Disconnessione da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
     <item msgid="699832486578171722">"Disconnessa"</item>
     <item msgid="522383512264986901">"Operazione non riuscita"</item>
     <item msgid="3602596701217484364">"Bloccato"</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 38eeda2..8987968 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -22,10 +22,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.hardware.usb.IUsbManager;
 import android.net.Uri;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -146,10 +148,23 @@
     /**
      * Returns a boolean indicating whether a given package is a mainline module.
      */
-    public static boolean isMainlineModule(Context context, String packageName) {
-        final PackageManager pm = context.getPackageManager();
+    public static boolean isMainlineModule(PackageManager pm, String packageName) {
+        // Check if the package is listed among the system modules.
         try {
-            return pm.getModuleInfo(packageName, 0 /* flags */) != null;
+            pm.getModuleInfo(packageName, 0 /* flags */);
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            //pass
+        }
+
+        try {
+            final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
+            // Check if the package is contained in an APEX. There is no public API to properly
+            // check whether a given APK package comes from an APEX registered as module.
+            // Therefore we conservatively assume that any package scanned from an /apex path is
+            // a system package.
+            return pkg.applicationInfo.sourceDir.startsWith(
+                    Environment.getApexDirectory().getAbsolutePath());
         } catch (PackageManager.NameNotFoundException e) {
             return false;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
old mode 100644
new mode 100755
index d48aa24..a0a91a1
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -242,7 +242,17 @@
         final TimeZoneNames.NameType nameType =
                 tz.inDaylightTime(now) ? TimeZoneNames.NameType.LONG_DAYLIGHT
                         : TimeZoneNames.NameType.LONG_STANDARD;
-        return names.getDisplayName(tz.getID(), nameType, now.getTime());
+        // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs
+        // that match ICUs zone IDs (which are similar but not guaranteed the same as those
+        // in timezones.xml). timezones.xml and related files uses the IANA IDs. ICU IDs are
+        // stable and IANA IDs have changed over time so they have drifted.
+        // See http://bugs.icu-project.org/trac/ticket/13070 / http://b/36469833.
+        String canonicalZoneId = android.icu.util.TimeZone.getCanonicalID(tz.getID());
+        if (canonicalZoneId == null) {
+            canonicalZoneId = tz.getID();
+        }
+
+        return names.getDisplayName(canonicalZoneId, nameType, now.getTime());
     }
 
     private static void appendWithTtsSpan(SpannableStringBuilder builder, CharSequence content,
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1faa16a..5f47cfe 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -243,6 +243,9 @@
     <!-- Default for Settings.Secure.AWARE_LOCK_ENABLED -->
     <bool name="def_aware_lock_enabled">false</bool>
 
+    <!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED -->
+    <bool name="def_hdmiControlAutoDeviceOff">false</bool>
+
     <!-- Default for Settings.System.TIME_12_24 -->
     <string name="def_time_format" translatable="false">12</string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 95c3e03..5322609 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3513,7 +3513,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 190;
+            private static final int SETTINGS_VERSION = 191;
 
             private final int mUserId;
 
@@ -4877,6 +4877,21 @@
                     currentVersion = 190;
                 }
 
+                if (currentVersion == 190) {
+                    // Version 190: get HDMI auto device off from overlay
+                    final SettingsState globalSettings = getGlobalSettingsLocked();
+                    final Setting currentSetting = globalSettings.getSettingLocked(
+                            Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
+                    if (currentSetting.isNull()) {
+                        globalSettings.insertSettingLocked(
+                                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                                getContext().getResources().getBoolean(
+                                        R.bool.def_hdmiControlAutoDeviceOff) ? "1" : "0",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 191;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 1a4dd1e..1db2e32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -25,12 +25,16 @@
 
 import static java.lang.Integer.max;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.ColorStateList;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
 import android.os.Handler;
@@ -48,9 +52,13 @@
 import android.view.ViewConfiguration;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.SpringAnimation;
@@ -64,6 +72,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -100,6 +109,8 @@
 
     private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
 
+    private static final long IME_DISAPPEAR_DURATION_MS = 125;
+
     private KeyguardSecurityModel mSecurityModel;
     private LockPatternUtils mLockPatternUtils;
 
@@ -125,6 +136,7 @@
     private int mActivePointerId = -1;
     private boolean mIsDragging;
     private float mStartTouchY = -1;
+    private boolean mDisappearAnimRunning;
 
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
             new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -148,22 +160,29 @@
                 public WindowInsets onProgress(WindowInsets windowInsets,
                         List<WindowInsetsAnimation> list) {
                     int translationY = 0;
-                    for (WindowInsetsAnimation animation : list) {
-                        if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
-                            continue;
+                    if (mDisappearAnimRunning) {
+                        mSecurityViewFlipper.setTranslationY(
+                                mInitialBounds.bottom - mFinalBounds.bottom);
+                    } else {
+                        for (WindowInsetsAnimation animation : list) {
+                            if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+                                continue;
+                            }
+                            final int paddingBottom = (int) MathUtils.lerp(
+                                    mInitialBounds.bottom - mFinalBounds.bottom, 0,
+                                    animation.getInterpolatedFraction());
+                            translationY += paddingBottom;
                         }
-                        final int paddingBottom = (int) MathUtils.lerp(
-                                mInitialBounds.bottom - mFinalBounds.bottom, 0,
-                                animation.getInterpolatedFraction());
-                        translationY += paddingBottom;
+                        mSecurityViewFlipper.setTranslationY(translationY);
                     }
-                    mSecurityViewFlipper.setTranslationY(translationY);
                     return windowInsets;
                 }
 
                 @Override
                 public void onEnd(WindowInsetsAnimation animation) {
-                    mSecurityViewFlipper.setTranslationY(0);
+                    if (!mDisappearAnimRunning) {
+                        mSecurityViewFlipper.setTranslationY(0);
+                    }
                 }
             };
 
@@ -376,8 +395,51 @@
     }
 
     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+        mDisappearAnimRunning = true;
         if (mCurrentSecuritySelection == SecurityMode.Password) {
-            mSecurityViewFlipper.getWindowInsetsController().hide(WindowInsets.Type.ime());
+            mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
+                    IME_DISAPPEAR_DURATION_MS,
+                    Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
+
+
+                        @Override
+                        public void onReady(@NonNull WindowInsetsAnimationController controller,
+                                int types) {
+                            ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+                            anim.addUpdateListener(animation -> {
+                                if (controller.isCancelled()) {
+                                    return;
+                                }
+                                Insets shownInsets = controller.getShownStateInsets();
+                                Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
+                                        (int) (-shownInsets.bottom / 4
+                                                * anim.getAnimatedFraction())));
+                                controller.setInsetsAndAlpha(insets,
+                                        (float) animation.getAnimatedValue(),
+                                        anim.getAnimatedFraction());
+                            });
+                            anim.addListener(new AnimatorListenerAdapter() {
+                                @Override
+                                public void onAnimationEnd(Animator animation) {
+                                    controller.finish(false);
+                                }
+                            });
+                            anim.setDuration(IME_DISAPPEAR_DURATION_MS);
+                            anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+                            anim.start();
+                        }
+
+                        @Override
+                        public void onFinished(
+                                @NonNull WindowInsetsAnimationController controller) {
+                            mDisappearAnimRunning = false;
+                        }
+
+                        @Override
+                        public void onCancelled(
+                                @Nullable WindowInsetsAnimationController controller) {
+                        }
+                    });
         }
         if (mCurrentSecuritySelection != SecurityMode.None) {
             return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
@@ -887,6 +949,7 @@
     @Override
     public void reset() {
         mSecurityViewFlipper.reset();
+        mDisappearAnimRunning = false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 6da7bc8..7f78ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.bubbles;
 
+import static android.app.Notification.FLAG_BUBBLE;
 import static android.os.AsyncTask.Status.FINISHED;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -28,19 +29,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Path;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.InstanceId;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
@@ -54,12 +57,17 @@
 class Bubble implements BubbleViewProvider {
     private static final String TAG = "Bubble";
 
+    /**
+     * NotificationEntry associated with the bubble. A null value implies this bubble is loaded
+     * from disk.
+     */
+    @Nullable
+    private NotificationEntry mEntry;
     private final String mKey;
 
     private long mLastUpdated;
     private long mLastAccessed;
 
-    @Nullable
     private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
 
     /** Whether the bubble should show a dot for the notification indicating updated content. */
@@ -67,6 +75,8 @@
 
     /** Whether flyout text should be suppressed, regardless of any other flags or state. */
     private boolean mSuppressFlyout;
+    /** Whether this bubble should auto expand regardless of the normal flag, used for overflow. */
+    private boolean mShouldAutoExpand;
 
     // Items that are typically loaded later
     private String mAppName;
@@ -82,7 +92,6 @@
      * Presentational info about the flyout.
      */
     public static class FlyoutMessage {
-        @Nullable public Icon senderIcon;
         @Nullable public Drawable senderAvatar;
         @Nullable public CharSequence senderName;
         @Nullable public CharSequence message;
@@ -100,39 +109,16 @@
     private UserHandle mUser;
     @NonNull
     private String mPackageName;
-    @Nullable
-    private String mTitle;
-    @Nullable
-    private Icon mIcon;
-    private boolean mIsBubble;
-    private boolean mIsVisuallyInterruptive;
-    private boolean mIsClearable;
-    private boolean mShouldSuppressNotificationDot;
-    private boolean mShouldSuppressNotificationList;
-    private boolean mShouldSuppressPeek;
     private int mDesiredHeight;
     @DimenRes
     private int mDesiredHeightResId;
 
-    /** for logging **/
-    @Nullable
-    private InstanceId mInstanceId;
-    @Nullable
-    private String mChannelId;
-    private int mNotificationId;
-    private int mAppUid = -1;
-
-    @Nullable
-    private PendingIntent mIntent;
-    @Nullable
-    private PendingIntent mDeleteIntent;
-
     /**
      * Create a bubble with limited information based on given {@link ShortcutInfo}.
      * Note: Currently this is only being used when the bubble is persisted to disk.
      */
     Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
-            final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
+            final int desiredHeight, final int desiredHeightResId) {
         Objects.requireNonNull(key);
         Objects.requireNonNull(shortcutInfo);
         mShortcutInfo = shortcutInfo;
@@ -140,10 +126,8 @@
         mFlags = 0;
         mUser = shortcutInfo.getUserHandle();
         mPackageName = shortcutInfo.getPackage();
-        mIcon = shortcutInfo.getIcon();
         mDesiredHeight = desiredHeight;
         mDesiredHeightResId = desiredHeightResId;
-        mTitle = title;
     }
 
     /** Used in tests when no UI is required. */
@@ -161,6 +145,12 @@
         return mKey;
     }
 
+    @Nullable
+    public NotificationEntry getEntry() {
+        return mEntry;
+    }
+
+    @NonNull
     public UserHandle getUser() {
         return mUser;
     }
@@ -213,7 +203,14 @@
 
     @Nullable
     public String getTitle() {
-        return mTitle;
+        final CharSequence titleCharSeq;
+        if (mEntry == null) {
+            titleCharSeq = null;
+        } else {
+            titleCharSeq = mEntry.getSbn().getNotification().extras.getCharSequence(
+                    Notification.EXTRA_TITLE);
+        }
+        return titleCharSeq != null ? titleCharSeq.toString() : null;
     }
 
     /**
@@ -334,44 +331,17 @@
     void setEntry(@NonNull final NotificationEntry entry) {
         Objects.requireNonNull(entry);
         Objects.requireNonNull(entry.getSbn());
+        mEntry = entry;
         mLastUpdated = entry.getSbn().getPostTime();
-        mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
+        mFlags = entry.getSbn().getNotification().flags;
         mPackageName = entry.getSbn().getPackageName();
         mUser = entry.getSbn().getUser();
-        mTitle = getTitle(entry);
-        mIsClearable = entry.isClearable();
-        mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
-        mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
-        mShouldSuppressPeek = entry.shouldSuppressPeek();
-        mChannelId = entry.getSbn().getNotification().getChannelId();
-        mNotificationId = entry.getSbn().getId();
-        mAppUid = entry.getSbn().getUid();
-        mInstanceId = entry.getSbn().getInstanceId();
-        mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
-        if (entry.getRanking() != null) {
-            mShortcutInfo = entry.getRanking().getShortcutInfo() != null
-                    ? entry.getRanking().getShortcutInfo() : mShortcutInfo;
-            mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
-        }
         if (entry.getBubbleMetadata() != null) {
-            mFlags = entry.getBubbleMetadata().getFlags();
             mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
             mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
-            mIcon = entry.getBubbleMetadata().getIcon();
-            mIntent = entry.getBubbleMetadata().getIntent();
-            mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
         }
     }
 
-    @Nullable
-    Icon getIcon() {
-        return mIcon;
-    }
-
-    boolean isVisuallyInterruptive() {
-        return mIsVisuallyInterruptive;
-    }
-
     /**
      * @return the last time this bubble was updated or accessed, whichever is most recent.
      */
@@ -394,19 +364,6 @@
         return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
     }
 
-    public InstanceId getInstanceId() {
-        return mInstanceId;
-    }
-
-    @Nullable
-    public String getChannelId() {
-        return mChannelId;
-    }
-
-    public int getNotificationId() {
-        return mNotificationId;
-    }
-
     /**
      * Should be invoked whenever a Bubble is accessed (selected while expanded).
      */
@@ -427,19 +384,24 @@
      * Whether this notification should be shown in the shade.
      */
     boolean showInShade() {
-        return !shouldSuppressNotification() || !mIsClearable;
+        if (mEntry == null) return false;
+        return !shouldSuppressNotification() || !mEntry.isClearable();
     }
 
     /**
      * Sets whether this notification should be suppressed in the shade.
      */
     void setSuppressNotification(boolean suppressNotification) {
+        if (mEntry == null) return;
         boolean prevShowInShade = showInShade();
+        Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+        int flags = data.getFlags();
         if (suppressNotification) {
-            mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+            flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
         } else {
-            mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+            flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
         }
+        data.setFlags(flags);
 
         if (showInShade() != prevShowInShade && mSuppressionListener != null) {
             mSuppressionListener.onBubbleNotificationSuppressionChange(this);
@@ -462,8 +424,9 @@
      */
     @Override
     public boolean showDot() {
+        if (mEntry == null) return false;
         return mShowBubbleUpdateDot
-                && !mShouldSuppressNotificationDot
+                && !mEntry.shouldSuppressNotificationDot()
                 && !shouldSuppressNotification();
     }
 
@@ -471,9 +434,10 @@
      * Whether the flyout for the bubble should be shown.
      */
     boolean showFlyout() {
-        return !mSuppressFlyout && !mShouldSuppressPeek
+        if (mEntry == null) return false;
+        return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
                 && !shouldSuppressNotification()
-                && !mShouldSuppressNotificationList;
+                && !mEntry.shouldSuppressNotificationList();
     }
 
     /**
@@ -516,14 +480,25 @@
         }
     }
 
-    @Nullable
-    PendingIntent getBubbleIntent() {
-        return mIntent;
+    /**
+     * Whether shortcut information should be used to populate the bubble.
+     * <p>
+     * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
+     * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
+     */
+    boolean usingShortcutInfo() {
+        return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
+                || mShortcutInfo != null;
     }
 
     @Nullable
-    PendingIntent getDeleteIntent() {
-        return mDeleteIntent;
+    PendingIntent getBubbleIntent() {
+        if (mEntry == null) return null;
+        Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+        if (data != null) {
+            return data.getIntent();
+        }
+        return null;
     }
 
     Intent getSettingsIntent(final Context context) {
@@ -539,12 +514,8 @@
         return intent;
     }
 
-    public int getAppUid() {
-        return mAppUid;
-    }
-
     private int getUid(final Context context) {
-        if (mAppUid != -1) return mAppUid;
+        if (mEntry != null) return mEntry.getSbn().getUid();
         final PackageManager pm = context.getPackageManager();
         if (pm == null) return -1;
         try {
@@ -577,27 +548,24 @@
     }
 
     private boolean shouldSuppressNotification() {
-        return isEnabled(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+        if (mEntry == null) return true;
+        return mEntry.getBubbleMetadata() != null
+                && mEntry.getBubbleMetadata().isNotificationSuppressed();
     }
 
-    public boolean shouldAutoExpand() {
-        return isEnabled(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+    boolean shouldAutoExpand() {
+        if (mEntry == null) return mShouldAutoExpand;
+        Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
+        return (metadata != null && metadata.getAutoExpandBubble()) ||  mShouldAutoExpand;
     }
 
     void setShouldAutoExpand(boolean shouldAutoExpand) {
-        if (shouldAutoExpand) {
-            enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-        } else {
-            disable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-        }
-    }
-
-    public void setIsBubble(final boolean isBubble) {
-        mIsBubble = isBubble;
+        mShouldAutoExpand = shouldAutoExpand;
     }
 
     public boolean isBubble() {
-        return mIsBubble;
+        if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0;
+        return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0;
     }
 
     public void enable(int option) {
@@ -608,10 +576,6 @@
         mFlags &= ~option;
     }
 
-    public boolean isEnabled(int option) {
-        return (mFlags & option) != 0;
-    }
-
     @Override
     public String toString() {
         return "Bubble{" + mKey + '}';
@@ -646,24 +610,34 @@
 
     @Override
     public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
-        SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                mPackageName,
-                mChannelId,
-                mNotificationId,
-                index,
-                bubbleCount,
-                action,
-                normalX,
-                normalY,
-                showInShade(),
-                false /* isOngoing (unused) */,
-                false /* isAppForeground (unused) */);
-    }
-
-    @Nullable
-    private static String getTitle(@NonNull final NotificationEntry e) {
-        final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
-                Notification.EXTRA_TITLE);
-        return titleCharSeq == null ? null : titleCharSeq.toString();
+        if (this.getEntry() == null
+                || this.getEntry().getSbn() == null) {
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    null /* package name */,
+                    null /* notification channel */,
+                    0 /* notification ID */,
+                    0 /* bubble position */,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    false /* unread bubble */,
+                    false /* on-going bubble */,
+                    false /* isAppForeground (unused) */);
+        } else {
+            StatusBarNotification notification = this.getEntry().getSbn();
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    notification.getPackageName(),
+                    notification.getNotification().getChannelId(),
+                    notification.getId(),
+                    index,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    this.showInShade(),
+                    false /* isOngoing (unused) */,
+                    false /* isAppForeground (unused) */);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index b2c5402..c4c5da4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -505,7 +505,8 @@
         addNotifCallback(new NotifCallback() {
             @Override
             public void removeNotification(NotificationEntry entry, int reason) {
-                mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason);
+                mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
+                        reason);
             }
 
             @Override
@@ -636,13 +637,8 @@
                 mStackView.setExpandListener(mExpandListener);
             }
 
-            mStackView.setUnbubbleConversationCallback(key -> {
-                final NotificationEntry entry =
-                        mNotificationEntryManager.getPendingOrActiveNotif(key);
-                if (entry != null) {
-                    onUserChangedBubble(entry, false /* shouldBubble */);
-                }
-            });
+            mStackView.setUnbubbleConversationCallback(notificationEntry ->
+                    onUserChangedBubble(notificationEntry, false /* shouldBubble */));
         }
 
         addToWindowManagerMaybe();
@@ -1028,7 +1024,10 @@
      * @param entry the notification to change bubble state for.
      * @param shouldBubble whether the notification should show as a bubble or not.
      */
-    public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) {
+    public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) {
+        if (entry == null) {
+            return;
+        }
         NotificationChannel channel = entry.getChannel();
         final String appPkg = entry.getSbn().getPackageName();
         final int appUid = entry.getSbn().getUid();
@@ -1104,8 +1103,7 @@
             mBubbleData.removeSuppressedSummary(groupKey);
 
             // Remove any associated bubble children with the summary
-            final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                    groupKey, mNotificationEntryManager);
+            final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
             for (int i = 0; i < bubbleChildren.size(); i++) {
                 removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
             }
@@ -1163,18 +1161,21 @@
 
     private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
         Objects.requireNonNull(b);
-        b.setIsBubble(isBubble);
-        final NotificationEntry entry = mNotificationEntryManager
-                .getPendingOrActiveNotif(b.getKey());
-        if (entry != null) {
+        if (isBubble) {
+            b.enable(FLAG_BUBBLE);
+        } else {
+            b.disable(FLAG_BUBBLE);
+        }
+        if (b.getEntry() != null) {
             // Updating the entry to be a bubble will trigger our normal update flow
-            setIsBubble(entry, isBubble, b.shouldAutoExpand());
+            setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand());
         } else if (isBubble) {
-            // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
-            // stack ourselves
+            // If we have no entry to update, it's a persisted bubble so
+            // we need to add it to the stack ourselves
             Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
             inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
                     !bubble.shouldAutoExpand() /* showInShade */);
+
         }
     }
 
@@ -1213,8 +1214,6 @@
                 if (reason == DISMISS_NOTIF_CANCEL) {
                     bubblesToBeRemovedFromRepository.add(bubble);
                 }
-                final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
-                        bubble.getKey());
                 if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
                     if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
                         && (!bubble.showInShade()
@@ -1223,27 +1222,26 @@
                         // The bubble is now gone & the notification is hidden from the shade, so
                         // time to actually remove it
                         for (NotifCallback cb : mCallbacks) {
-                            if (entry != null) {
-                                cb.removeNotification(entry, REASON_CANCEL);
+                            if (bubble.getEntry() != null) {
+                                cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
                             }
                         }
                     } else {
                         if (bubble.isBubble()) {
                             setIsBubble(bubble, false /* isBubble */);
                         }
-                        if (entry != null && entry.getRow() != null) {
-                            entry.getRow().updateBubbleButton();
+                        if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) {
+                            bubble.getEntry().getRow().updateBubbleButton();
                         }
                     }
 
                 }
-                if (entry != null) {
-                    final String groupKey = entry.getSbn().getGroupKey();
-                    if (mBubbleData.getBubblesInGroup(
-                            groupKey, mNotificationEntryManager).isEmpty()) {
+                if (bubble.getEntry() != null) {
+                    final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+                    if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
                         // Time to potentially remove the summary
                         for (NotifCallback cb : mCallbacks) {
-                            cb.maybeCancelSummary(entry);
+                            cb.maybeCancelSummary(bubble.getEntry());
                         }
                     }
                 }
@@ -1268,12 +1266,9 @@
 
             if (update.selectionChanged && mStackView != null) {
                 mStackView.setSelectedBubble(update.selectedBubble);
-                if (update.selectedBubble != null) {
-                    final NotificationEntry entry = mNotificationEntryManager
-                            .getPendingOrActiveNotif(update.selectedBubble.getKey());
-                    if (entry != null) {
-                        mNotificationGroupManager.updateSuppression(entry);
-                    }
+                if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
+                    mNotificationGroupManager.updateSuppression(
+                            update.selectedBubble.getEntry());
                 }
             }
 
@@ -1346,8 +1341,7 @@
         }
 
         String groupKey = entry.getSbn().getGroupKey();
-        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                groupKey, mNotificationEntryManager);
+        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
         boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
                 && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
         boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1367,15 +1361,9 @@
                     // As far as group manager is concerned, once a child is no longer shown
                     // in the shade, it is essentially removed.
                     Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
-                    if (bubbleChild != null) {
-                        final NotificationEntry entry = mNotificationEntryManager
-                                .getPendingOrActiveNotif(bubbleChild.getKey());
-                        if (entry != null) {
-                            mNotificationGroupManager.onEntryRemoved(entry);
-                        }
-                        bubbleChild.setSuppressNotification(true);
-                        bubbleChild.setShowDot(false /* show */);
-                    }
+                    mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
+                    bubbleChild.setSuppressNotification(true);
+                    bubbleChild.setShowDot(false /* show */);
                 } else {
                     // non-bubbled children can be removed
                     for (NotifCallback cb : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index c870612..20a9a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
+import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.util.Log;
@@ -33,7 +34,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController.DismissReason;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
@@ -256,7 +256,8 @@
         }
         mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
         Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
-        suppressFlyout |= !bubble.isVisuallyInterruptive();
+        suppressFlyout |= bubble.getEntry() == null
+                || !bubble.getEntry().getRanking().visuallyInterruptive();
 
         if (prevBubble == null) {
             // Create a new bubble
@@ -334,15 +335,13 @@
      * Retrieves any bubbles that are part of the notification group represented by the provided
      * group key.
      */
-    ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
-            NotificationEntryManager nem) {
+    ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) {
         ArrayList<Bubble> bubbleChildren = new ArrayList<>();
         if (groupKey == null) {
             return bubbleChildren;
         }
         for (Bubble b : mBubbles) {
-            final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
-            if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
+            if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
                 bubbleChildren.add(b);
             }
         }
@@ -440,7 +439,9 @@
             Bubble newSelected = mBubbles.get(newIndex);
             setSelectedBubbleInternal(newSelected);
         }
-        maybeSendDeleteIntent(reason, bubbleToRemove);
+        if (bubbleToRemove.getEntry() != null) {
+            maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
+        }
     }
 
     void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -610,14 +611,21 @@
         return true;
     }
 
-    private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) {
-        if (reason != BubbleController.DISMISS_USER_GESTURE) return;
-        PendingIntent deleteIntent = bubble.getDeleteIntent();
-        if (deleteIntent == null) return;
-        try {
-            deleteIntent.send();
-        } catch (PendingIntent.CanceledException e) {
-            Log.w(TAG, "Failed to send delete intent for bubble with key: " + bubble.getKey());
+    private void maybeSendDeleteIntent(@DismissReason int reason,
+            @NonNull final NotificationEntry entry) {
+        if (reason == BubbleController.DISMISS_USER_GESTURE) {
+            Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
+            PendingIntent deleteIntent = bubbleMetadata != null
+                    ? bubbleMetadata.getDeleteIntent()
+                    : null;
+            if (deleteIntent != null) {
+                try {
+                    deleteIntent.send();
+                } catch (PendingIntent.CanceledException e) {
+                    Log.w(TAG, "Failed to send delete intent for bubble with key: "
+                            + entry.getKey());
+                }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index 0c25d14..d20f405 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -74,15 +74,11 @@
 
     private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
         return bubbles.mapNotNull { b ->
-            BubbleEntity(
-                    userId,
-                    b.packageName,
-                    b.shortcutInfo?.id ?: return@mapNotNull null,
-                    b.key,
-                    b.rawDesiredHeight,
-                    b.rawDesiredHeightResId,
-                    b.title
-            )
+            var shortcutId = b.shortcutInfo?.id
+            if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
+            if (shortcutId == null) return@mapNotNull null
+            BubbleEntity(userId, b.packageName, shortcutId, b.key, b.rawDesiredHeight,
+                    b.rawDesiredHeightResId)
         }
     }
 
@@ -163,13 +159,8 @@
         val bubbles = entities.mapNotNull { entity ->
             shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
                     ?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
-                    ?.let { shortcutInfo -> Bubble(
-                            entity.key,
-                            shortcutInfo,
-                            entity.desiredHeight,
-                            entity.desiredHeightResId,
-                            entity.title
-                    ) }
+                    ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo, entity.desiredHeight,
+                            entity.desiredHeightResId) }
         }
         uiScope.launch { cb(bubbles) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 2f7ffde..ca359b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -169,7 +169,7 @@
                             return;
                         }
                         try {
-                            if (!mIsOverflow && mBubble.getShortcutInfo() != null) {
+                            if (!mIsOverflow && mBubble.usingShortcutInfo()) {
                                 options.setApplyActivityFlagsForBubbles(true);
                                 mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
                                         options, null /* sourceBounds */);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 1fa3aaa..8c76cda 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -31,7 +31,6 @@
 import android.graphics.Path;
 import android.graphics.PointF;
 import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -224,10 +223,9 @@
             float[] dotCenter,
             boolean hideDot) {
 
-        final Drawable senderAvatar = flyoutMessage.senderAvatar;
-        if (senderAvatar != null && flyoutMessage.isGroupChat) {
+        if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
             mSenderAvatar.setVisibility(VISIBLE);
-            mSenderAvatar.setImageDrawable(senderAvatar);
+            mSenderAvatar.setImageDrawable(flyoutMessage.senderAvatar);
         } else {
             mSenderAvatar.setVisibility(GONE);
             mSenderAvatar.setTranslationX(0);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index a799f2d..74231c6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -15,8 +15,7 @@
  */
 package com.android.systemui.bubbles;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
+import android.app.Notification;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
@@ -51,14 +50,15 @@
     /**
      * Returns the drawable that the developer has provided to display in the bubble.
      */
-    Drawable getBubbleDrawable(@NonNull final Context context,
-            @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
+    Drawable getBubbleDrawable(Context context, ShortcutInfo shortcutInfo,
+            Notification.BubbleMetadata metadata) {
         if (shortcutInfo != null) {
             LauncherApps launcherApps =
                     (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
             int density = context.getResources().getConfiguration().densityDpi;
             return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
         } else {
+            Icon ic = metadata.getIcon();
             if (ic != null) {
                 if (ic.getType() == Icon.TYPE_URI
                         || ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index c1dd8c3..c5faae0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bubbles;
 
+import android.service.notification.StatusBarNotification;
+
 import com.android.internal.logging.UiEventLoggerImpl;
 
 /**
@@ -30,11 +32,12 @@
      * @param e UI event
      */
     public void log(Bubble b, UiEventEnum e) {
-        if (b.getInstanceId() == null) {
+        if (b.getEntry() == null) {
             // Added from persistence -- TODO log this with specific event?
             return;
         }
-        logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
+        StatusBarNotification sbn = b.getEntry().getSbn();
+        logWithInstanceId(e, sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 0b25c44..b644079 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -291,7 +291,9 @@
                 });
 
         // If the bubble was persisted, the entry is null but it should have shortcut info
-        ShortcutInfo info = b.getShortcutInfo();
+        ShortcutInfo info = b.getEntry() == null
+                ? b.getShortcutInfo()
+                : b.getEntry().getRanking().getShortcutInfo();
         if (info == null) {
             Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0e6ce5c..23bd174 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -92,6 +92,7 @@
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.util.DismissCircleView;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -293,7 +294,7 @@
     private BubbleController.BubbleExpandListener mExpandListener;
 
     /** Callback to run when we want to unbubble the given notification's conversation. */
-    private Consumer<String> mUnbubbleConversationCallback;
+    private Consumer<NotificationEntry> mUnbubbleConversationCallback;
 
     private SysUiState mSysUiState;
 
@@ -1013,7 +1014,10 @@
         mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener(
                 view -> {
                     showManageMenu(false /* show */);
-                    mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey());
+                    final Bubble bubble = mBubbleData.getSelectedBubble();
+                    if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+                        mUnbubbleConversationCallback.accept(bubble.getEntry());
+                    }
                 });
 
         mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
@@ -1365,7 +1369,7 @@
 
     /** Sets the function to call to un-bubble the given conversation. */
     public void setUnbubbleConversationCallback(
-            Consumer<String> unbubbleConversationCallback) {
+            Consumer<NotificationEntry> unbubbleConversationCallback) {
         mUnbubbleConversationCallback = unbubbleConversationCallback;
     }
 
@@ -1905,7 +1909,9 @@
                     updateOverflowVisibility();
 
                     afterExpandedViewAnimation();
-                    previouslySelected.setContentVisibility(false);
+                    if (previouslySelected != null) {
+                        previouslySelected.setContentVisibility(false);
+                    }
                 })
                 .start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 3e4ff52..525d5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -37,6 +37,8 @@
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
 import android.os.Parcelable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PathParser;
@@ -51,7 +53,6 @@
 
 import java.lang.ref.WeakReference;
 import java.util.List;
-import java.util.Objects;
 
 /**
  * Simple task to inflate views & load necessary info to display a bubble.
@@ -128,10 +129,35 @@
         @Nullable
         static BubbleViewInfo populate(Context c, BubbleStackView stackView,
                 BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
+            final NotificationEntry entry = b.getEntry();
+            if (entry == null) {
+                // populate from ShortcutInfo when NotificationEntry is not available
+                final ShortcutInfo s = b.getShortcutInfo();
+                return populate(c, stackView, iconFactory, skipInflation || b.isInflated(),
+                        s.getPackage(), s.getUserHandle(), s, null);
+            }
+            final StatusBarNotification sbn = entry.getSbn();
+            final String bubbleShortcutId =  entry.getBubbleMetadata().getShortcutId();
+            final ShortcutInfo si = bubbleShortcutId == null
+                    ? null : entry.getRanking().getShortcutInfo();
+            return populate(
+                    c, stackView, iconFactory, skipInflation || b.isInflated(),
+                    sbn.getPackageName(), sbn.getUser(), si, entry);
+        }
+
+        private static BubbleViewInfo populate(
+                @NonNull final Context c,
+                @NonNull final BubbleStackView stackView,
+                @NonNull final BubbleIconFactory iconFactory,
+                final boolean isInflated,
+                @NonNull final String packageName,
+                @NonNull final UserHandle user,
+                @Nullable final ShortcutInfo shortcutInfo,
+                @Nullable final NotificationEntry entry) {
             BubbleViewInfo info = new BubbleViewInfo();
 
             // View inflation: only should do this once per bubble
-            if (!skipInflation && !b.isInflated()) {
+            if (!isInflated) {
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.imageView = (BadgedImageView) inflater.inflate(
                         R.layout.bubble_view, stackView, false /* attachToRoot */);
@@ -141,8 +167,8 @@
                 info.expandedView.setStackView(stackView);
             }
 
-            if (b.getShortcutInfo() != null) {
-                info.shortcutInfo = b.getShortcutInfo();
+            if (shortcutInfo != null) {
+                info.shortcutInfo = shortcutInfo;
             }
 
             // App name & app icon
@@ -152,7 +178,7 @@
             Drawable appIcon;
             try {
                 appInfo = pm.getApplicationInfo(
-                        b.getPackageName(),
+                        packageName,
                         PackageManager.MATCH_UNINSTALLED_PACKAGES
                                 | PackageManager.MATCH_DISABLED_COMPONENTS
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -160,17 +186,17 @@
                 if (appInfo != null) {
                     info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
                 }
-                appIcon = pm.getApplicationIcon(b.getPackageName());
-                badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
+                appIcon = pm.getApplicationIcon(packageName);
+                badgedIcon = pm.getUserBadgedIcon(appIcon, user);
             } catch (PackageManager.NameNotFoundException exception) {
                 // If we can't find package... don't think we should show the bubble.
-                Log.w(TAG, "Unable to find package: " + b.getPackageName());
+                Log.w(TAG, "Unable to find package: " + packageName);
                 return null;
             }
 
             // Badged bubble image
             Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
-                    b.getIcon());
+                    entry == null ? null : entry.getBubbleMetadata());
             if (bubbleDrawable == null) {
                 // Default to app icon
                 bubbleDrawable = appIcon;
@@ -196,10 +222,8 @@
                     Color.WHITE, WHITE_SCRIM_ALPHA);
 
             // Flyout
-            info.flyoutMessage = b.getFlyoutMessage();
-            if (info.flyoutMessage != null) {
-                info.flyoutMessage.senderAvatar =
-                        loadSenderAvatar(c, info.flyoutMessage.senderIcon);
+            if (entry != null) {
+                info.flyoutMessage = extractFlyoutMessage(c, entry);
             }
             return info;
         }
@@ -211,8 +235,8 @@
      * notification, based on its type. Returns null if there should not be an update message.
      */
     @NonNull
-    static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
-        Objects.requireNonNull(entry);
+    static Bubble.FlyoutMessage extractFlyoutMessage(Context context,
+            NotificationEntry entry) {
         final Notification underlyingNotif = entry.getSbn().getNotification();
         final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
 
@@ -240,9 +264,20 @@
                 if (latestMessage != null) {
                     bubbleMessage.message = latestMessage.getText();
                     Person sender = latestMessage.getSenderPerson();
-                    bubbleMessage.senderName = sender != null ? sender.getName() : null;
+                    bubbleMessage.senderName = sender != null
+                            ? sender.getName()
+                            : null;
+
                     bubbleMessage.senderAvatar = null;
-                    bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
+                    if (sender != null && sender.getIcon() != null) {
+                        if (sender.getIcon().getType() == Icon.TYPE_URI
+                                || sender.getIcon().getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+                            context.grantUriPermission(context.getPackageName(),
+                                    sender.getIcon().getUri(),
+                                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                        }
+                        bubbleMessage.senderAvatar = sender.getIcon().loadDrawable(context);
+                    }
                     return bubbleMessage;
                 }
             } else if (Notification.InboxStyle.class.equals(style)) {
@@ -271,15 +306,4 @@
 
         return bubbleMessage;
     }
-
-    @Nullable
-    static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
-        Objects.requireNonNull(context);
-        if (icon == null) return null;
-        if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
-            context.grantUriPermission(context.getPackageName(),
-                    icon.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        }
-        return icon.loadDrawable(context);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
index 24768cd..355c4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
@@ -24,6 +24,5 @@
     val shortcutId: String,
     val key: String,
     val desiredHeight: Int,
-    @DimenRes val desiredHeightResId: Int,
-    val title: String? = null
+    @DimenRes val desiredHeightResId: Int
 )
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
index 66fff338..a8faf25 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
@@ -33,7 +33,6 @@
 private const val ATTR_KEY = "key"
 private const val ATTR_DESIRED_HEIGHT = "h"
 private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
-private const val ATTR_TITLE = "t"
 
 /**
  * Writes the bubbles in xml format into given output stream.
@@ -64,7 +63,6 @@
         serializer.attribute(null, ATTR_KEY, bubble.key)
         serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
         serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
-        bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
         serializer.endTag(null, TAG_BUBBLE)
     } catch (e: IOException) {
         throw RuntimeException(e)
@@ -94,8 +92,7 @@
             parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null,
             parser.getAttributeWithName(ATTR_KEY) ?: return null,
             parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
-            parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
-            parser.getAttributeWithName(ATTR_TITLE)
+            parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
rename to packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
index 693c270..d2776d2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util
+package com.android.systemui.controls
 
 import android.os.UserHandle
 
@@ -23,8 +23,6 @@
  * changes.
  */
 interface UserAwareController {
-    @JvmDefault
     fun changeUser(newUser: UserHandle) {}
-
     val currentUserId: Int
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index eed5531..d4d4d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -20,7 +20,7 @@
 import android.service.controls.Control
 import android.service.controls.ControlsProviderService
 import android.service.controls.actions.ControlAction
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
 import java.util.function.Consumer
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 496741b..45ba1e6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -21,7 +21,7 @@
 import android.service.controls.ControlsProviderService
 import android.service.controls.actions.ControlAction
 import com.android.systemui.controls.ControlStatus
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
 import com.android.systemui.controls.management.ControlsFavoritingActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import java.util.function.Consumer
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 26124f7..0543319 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -238,7 +238,7 @@
             updateFavorite(!favorite.isChecked)
             favoriteCallback(wrapper.controlId, favorite.isChecked)
         }
-        applyRenderInfo(renderInfo)
+        applyRenderInfo(renderInfo, wrapper.deviceType)
     }
 
     override fun updateFavorite(favorite: Boolean) {
@@ -254,12 +254,16 @@
         return RenderInfo.lookup(itemView.context, component, deviceType)
     }
 
-    private fun applyRenderInfo(ri: RenderInfo) {
+    private fun applyRenderInfo(ri: RenderInfo, @DeviceTypes.DeviceType deviceType: Int) {
         val context = itemView.context
         val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
 
         icon.setImageDrawable(ri.icon)
-        icon.setImageTintList(fg)
+
+        // Do not color app icons
+        if (deviceType != DeviceTypes.TYPE_ROUTINE) {
+            icon.setImageTintList(fg)
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index b9f1666..647dacc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName
 import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
 import com.android.systemui.statusbar.policy.CallbackController
 
 /**
@@ -26,7 +26,7 @@
  */
 interface ControlsListingController :
         CallbackController<ControlsListingController.ControlsListingCallback>,
-    UserAwareController {
+        UserAwareController {
 
     /**
      * @return the current list of services that satisfies the [ServiceListing].
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index ee02b85..e853027 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -274,7 +274,6 @@
         val ri = RenderInfo.lookup(context, cws.componentName, deviceTypeOrError, offset)
         val fg = context.resources.getColorStateList(ri.foreground, context.theme)
         val newText = nextStatusText
-        nextStatusText = ""
         val control = cws.control
 
         var shouldAnimate = animated
@@ -297,10 +296,8 @@
         if (immediately) {
             status.alpha = STATUS_ALPHA_ENABLED
             status.text = text
-            nextStatusText = ""
-        } else {
-            nextStatusText = text
         }
+        nextStatusText = text
     }
 
     private fun animateBackgroundChange(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
index 48f9458..9dd0f53 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -21,6 +21,7 @@
 import android.view.View
 import android.service.controls.Control
 import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.StatelessTemplate
 
 import com.android.systemui.R
 import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
@@ -35,24 +36,44 @@
     lateinit var template: ControlTemplate
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
+    private var statelessTouch = false
+    private var lastColorOffset = 0
+    private val enabled: Boolean
+        get() = if (lastColorOffset > 0 || statelessTouch) true else false
+
+    companion object {
+        const val STATELESS_ENABLE_TIMEOUT_IN_MILLIS = 3000L
+    }
 
     override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
 
         cvh.layout.setOnClickListener(View.OnClickListener() {
             cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
+
+            // StatelessTemplates have no state, with no way to discern between enabled and
+            // disabled. Render an enabled state for a few moments to let the user know the
+            // action is in progress.
+            if (template is StatelessTemplate) {
+                statelessTouch = true
+                cvh.applyRenderInfo(enabled, lastColorOffset)
+                cvh.uiExecutor.executeDelayed({
+                    statelessTouch = false
+                    cvh.applyRenderInfo(enabled, lastColorOffset)
+                }, STATELESS_ENABLE_TIMEOUT_IN_MILLIS)
+            }
         })
     }
 
     override fun bind(cws: ControlWithState, colorOffset: Int) {
         this.control = cws.control!!
+        lastColorOffset = colorOffset
         cvh.setStatusText(control.getStatusText())
         template = control.getControlTemplate()
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
 
-        val enabled = if (colorOffset > 0) true else false
         clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
         cvh.applyRenderInfo(enabled, colorOffset)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index a2086e8..16f76de 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -68,7 +68,7 @@
     public static LogBuffer provideNotificationSectionLogBuffer(
             LogcatEchoTracker bufferFilter,
             DumpManager dumpManager) {
-        LogBuffer buffer = new LogBuffer("NotifSectionLog", 500, 10, bufferFilter);
+        LogBuffer buffer = new LogBuffer("NotifSectionLog", 1000, 10, bufferFilter);
         buffer.attach(dumpManager);
         return buffer;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 5008100..d5a2837 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -257,16 +257,18 @@
         rect.setColor(Color.TRANSPARENT);
 
         final MediaDeviceData device = data.getDevice();
+        int seamlessId = mViewHolder.getSeamless().getId();
         if (device != null && !device.getEnabled()) {
             mViewHolder.getSeamless().setEnabled(false);
-            // TODO(b/156875717): setEnabled should cause the alpha to change.
-            mViewHolder.getSeamless().setAlpha(0.38f);
+            expandedSet.setAlpha(seamlessId, 0.38f);
+            collapsedSet.setAlpha(seamlessId, 0.38f);
             iconView.setImageResource(R.drawable.ic_hardware_speaker);
             iconView.setVisibility(View.VISIBLE);
             deviceName.setText(R.string.media_seamless_remote_device);
         } else if (device != null) {
             mViewHolder.getSeamless().setEnabled(true);
-            mViewHolder.getSeamless().setAlpha(1f);
+            expandedSet.setAlpha(seamlessId, 1.0f);
+            collapsedSet.setAlpha(seamlessId, 1.0f);
             Drawable icon = device.getIcon();
             iconView.setVisibility(View.VISIBLE);
 
@@ -282,7 +284,8 @@
             // Reset to default
             Log.w(TAG, "device is null. Not binding output chip.");
             mViewHolder.getSeamless().setEnabled(true);
-            mViewHolder.getSeamless().setAlpha(1f);
+            expandedSet.setAlpha(seamlessId, 1.0f);
+            collapsedSet.setAlpha(seamlessId, 1.0f);
             iconView.setVisibility(View.GONE);
             deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index d077666..856c192 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -491,10 +491,11 @@
             Log.d(TAG, "resizeAndAnimatePipUnchecked: toBounds=" + toBounds
                     + " duration=" + duration + " callers=\n" + Debug.getCallers(5, "    "));
         }
-        if (!toBounds.equals(mBounds)) {
-            mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
-            setAnimatingToBounds(toBounds);
-        }
+
+        // Intentionally resize here even if the current bounds match the destination bounds.
+        // This is so all the proper callbacks are performed.
+        mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
+        setAnimatingToBounds(toBounds);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index f6b212c..c151715 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -260,12 +260,14 @@
 
     private void onMotionEvent(MotionEvent ev) {
         int action = ev.getActionMasked();
+        float x = ev.getX();
+        float y = ev.getY();
         if (action == MotionEvent.ACTION_DOWN) {
             mLastResizeBounds.setEmpty();
-            mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+            mAllowGesture = isWithinTouchRegion((int) x, (int) y);
             if (mAllowGesture) {
-                setCtrlType((int) ev.getX(), (int) ev.getY());
-                mDownPoint.set(ev.getX(), ev.getY());
+                setCtrlType((int) x, (int) y);
+                mDownPoint.set(x, y);
                 mLastDownBounds.set(mMotionHelper.getBounds());
             }
 
@@ -277,20 +279,23 @@
                     break;
                 case MotionEvent.ACTION_MOVE:
                     // Capture inputs
-                    float dx = Math.abs(ev.getX() - mDownPoint.x);
-                    float dy = Math.abs(ev.getY() - mDownPoint.y);
-                    if (!mThresholdCrossed && dx > mTouchSlop && dy > mTouchSlop) {
+                    if (!mThresholdCrossed
+                            && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
                         mThresholdCrossed = true;
+                        // Reset the down to begin resizing from this point
+                        mDownPoint.set(x, y);
                         mInputMonitor.pilferPointers();
                     }
-                    final Rect currentPipBounds = mMotionHelper.getBounds();
-                    mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
-                            mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
-                            mMinSize.y, mMaxSize, true,
-                            mLastDownBounds.width() > mLastDownBounds.height()));
-                    mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
-                    mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
-                            null);
+                    if (mThresholdCrossed) {
+                        final Rect currentPipBounds = mMotionHelper.getBounds();
+                        mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
+                                mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+                                mMinSize.y, mMaxSize, true,
+                                mLastDownBounds.width() > mLastDownBounds.height()));
+                        mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+                        mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
+                                null);
+                    }
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index 0a84f5e..2365e67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -31,7 +30,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
-import com.android.systemui.util.UserAwareController;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -39,7 +37,7 @@
 
 import javax.inject.Inject;
 
-public class AutoAddTracker implements UserAwareController {
+public class AutoAddTracker {
 
     private static final String[][] CONVERT_PREFS = {
             {Key.QS_HOTSPOT_ADDED, HOTSPOT},
@@ -51,39 +49,20 @@
 
     private final ArraySet<String> mAutoAdded;
     private final Context mContext;
-    private int mUserId;
 
-    public AutoAddTracker(Context context, int userId) {
+    @Inject
+    public AutoAddTracker(Context context) {
         mContext = context;
-        mUserId = userId;
         mAutoAdded = new ArraySet<>(getAdded());
         // TODO: remove migration code and shared preferences keys after P release
-        if (mUserId == UserHandle.USER_SYSTEM) {
-            for (String[] convertPref : CONVERT_PREFS) {
-                if (Prefs.getBoolean(context, convertPref[0], false)) {
-                    setTileAdded(convertPref[1]);
-                    Prefs.remove(context, convertPref[0]);
-                }
+        for (String[] convertPref : CONVERT_PREFS) {
+            if (Prefs.getBoolean(context, convertPref[0], false)) {
+                setTileAdded(convertPref[1]);
+                Prefs.remove(context, convertPref[0]);
             }
         }
         mContext.getContentResolver().registerContentObserver(
-                Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
-                UserHandle.USER_ALL);
-    }
-
-    @Override
-    public void changeUser(UserHandle newUser) {
-        if (newUser.getIdentifier() == mUserId) {
-            return;
-        }
-        mUserId = newUser.getIdentifier();
-        mAutoAdded.clear();
-        mAutoAdded.addAll(getAdded());
-    }
-
-    @Override
-    public int getCurrentUserId() {
-        return mUserId;
+                Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver);
     }
 
     public boolean isAdded(String tile) {
@@ -107,13 +86,12 @@
     }
 
     private void saveTiles() {
-        Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
-                TextUtils.join(",", mAutoAdded), mUserId);
+        Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
+                TextUtils.join(",", mAutoAdded));
     }
 
     private Collection<String> getAdded() {
-        String current = Secure.getStringForUser(mContext.getContentResolver(),
-                Secure.QS_AUTO_ADDED_TILES, mUserId);
+        String current = Secure.getString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES);
         if (current == null) {
             return Collections.emptyList();
         }
@@ -124,27 +102,7 @@
     protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange) {
-            mAutoAdded.clear();
             mAutoAdded.addAll(getAdded());
         }
     };
-
-    public static class Builder {
-        private final Context mContext;
-        private int mUserId;
-
-        @Inject
-        public Builder(Context context) {
-            mContext = context;
-        }
-
-        public Builder setUserId(int userId) {
-            mUserId = userId;
-            return this;
-        }
-
-        public AutoAddTracker build() {
-            return new AutoAddTracker(mContext, mUserId);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 2836992..3eed8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -291,15 +291,7 @@
     }
 
     private void emptyAndInflateOrRemovePages() {
-        final int nTiles = mTiles.size();
-        // We should always have at least one page, even if it's empty.
-        int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
-
-        // Add one more not full page if needed
-        if (nTiles > numPages * mPages.get(0).maxTiles()) {
-            numPages++;
-        }
-
+        final int numPages = getNumPages();
         final int NP = mPages.size();
         for (int i = 0; i < NP; i++) {
             mPages.get(i).removeAllViews();
@@ -431,6 +423,22 @@
         return mPages.get(0).mColumns;
     }
 
+    /**
+     * Gets the number of pages in this paged tile layout
+     */
+    public int getNumPages() {
+        final int nTiles = mTiles.size();
+        // We should always have at least one page, even if it's empty.
+        int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
+
+        // Add one more not full page if needed
+        if (nTiles > numPages * mPages.get(0).maxTiles()) {
+            numPages++;
+        }
+
+        return numPages;
+    }
+
     public int getNumVisibleTiles() {
         if (mPages.size() == 0) return 0;
         TilePage currentPage = mPages.get(getCurrentPageNumber());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index fc8e36f..c4bb4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -218,7 +218,7 @@
                 .addFloat(mActionsContainer, "alpha", 0, 1)
                 .addFloat(mEditContainer, "alpha", 0, 1)
                 .addFloat(mPageIndicator, "alpha", 0, 1)
-                .setStartDelay(0.15f)
+                .setStartDelay(0.9f)
                 .build();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 32f4b6c..d3f59ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -240,8 +240,13 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
         if (mTileLayout instanceof PagedTileLayout) {
+            // Since PageIndicator gets measured before PagedTileLayout, we preemptively set the
+            // # of pages before the measurement pass so PageIndicator is measured appropriately
+            if (mFooterPageIndicator != null) {
+                mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages());
+            }
+
             // Allow the UI to be as big as it want's to, we're in a scroll view
             int newHeight = 10000;
             int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 858a7e2..65d3572 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -255,9 +255,6 @@
         int currentUser = ActivityManager.getCurrentUser();
         if (currentUser != mCurrentUser) {
             mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
-            if (mAutoTiles != null) {
-                mAutoTiles.changeUser(UserHandle.of(currentUser));
-            }
         }
         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
         mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 311eda2..b5afe77 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -15,6 +15,7 @@
 package com.android.systemui.qs;
 
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
@@ -41,6 +42,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.DisplayCutout;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
@@ -347,6 +349,15 @@
                 com.android.internal.R.dimen.quick_qs_offset_height);
         mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
 
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (mQsDisabled) {
+            lp.height = resources.getDimensionPixelSize(
+                    com.android.internal.R.dimen.quick_qs_offset_height);
+        } else {
+            lp.height = WRAP_CONTENT;
+        }
+        setLayoutParams(lp);
+
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index f3e2f10..f89185e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -118,6 +118,7 @@
                 try {
                     if (mOverviewProxyService.getProxy() != null) {
                         mOverviewProxyService.getProxy().onOverviewToggle();
+                        mOverviewProxyService.notifyToggleRecentApps();
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 8a012b8..790b2585 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -200,11 +200,13 @@
                             mInputFocusTransferStartY = event.getY();
                             mInputFocusTransferStartMillis = event.getEventTime();
                             statusBar.onInputFocusTransfer(
-                                    mInputFocusTransferStarted, 0 /* velocity */);
+                                    mInputFocusTransferStarted, false /* cancel */,
+                                    0 /* velocity */);
                         }
                         if (action == ACTION_UP || action == ACTION_CANCEL) {
                             mInputFocusTransferStarted = false;
                             statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
+                                    action == ACTION_CANCEL,
                                     (event.getY() - mInputFocusTransferStartY)
                                     / (event.getEventTime() - mInputFocusTransferStartMillis));
                         }
@@ -692,7 +694,8 @@
             mHandler.post(()-> {
                 mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
                     mInputFocusTransferStarted = false;
-                    statusBarLazy.get().onInputFocusTransfer(false, 0 /* velocity */);
+                    statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
+                            0 /* velocity */);
                 });
             });
         }
@@ -871,6 +874,12 @@
         }
     }
 
+    void notifyToggleRecentApps() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onToggleRecentApps();
+        }
+    }
+
     private void updateEnabledState() {
         mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
                 MATCH_SYSTEM_ONLY,
@@ -901,6 +910,8 @@
         default void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {}
         default void onOverviewShown(boolean fromHome) {}
         default void onQuickScrubStarted() {}
+        /** Notify the recents app (overview) is started by 3-button navigation. */
+        default void onToggleRecentApps() {}
         /** Notify changes in the nav bar button alpha */
         default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
         default void onSystemUiStateChanged(int sysuiStateFlags) {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7babe2f..ca37532 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -215,7 +215,6 @@
     private Animator mScreenshotAnimation;
     private Runnable mOnCompleteRunnable;
     private Animator mDismissAnimation;
-    private SavedImageData mImageData;
     private boolean mInDarkMode = false;
     private boolean mDirectionLTR = true;
     private boolean mOrientationPortrait = true;
@@ -234,9 +233,6 @@
             switch (msg.what) {
                 case MESSAGE_CORNER_TIMEOUT:
                     mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
-                    if (mImageData != null) {
-                        mNotificationsController.showSilentScreenshotNotification(mImageData);
-                    }
                     GlobalScreenshot.this.dismissScreenshot("timeout", false);
                     mOnCompleteRunnable.run();
                     break;
@@ -408,9 +404,6 @@
         mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
         mDismissButton.setOnClickListener(view -> {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
-            if (mImageData != null) {
-                mNotificationsController.showSilentScreenshotNotification(mImageData);
-            }
             dismissScreenshot("dismiss_button", false);
             mOnCompleteRunnable.run();
         });
@@ -450,10 +443,6 @@
             });
         }
 
-        mImageData = null; // make sure we clear the current stored data
-        mNotificationsController.reset();
-        mNotificationsController.setImage(mScreenBitmap);
-
         mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
         mSaveInBgTask.execute();
     }
@@ -670,7 +659,6 @@
      */
     private void showUiOnActionsReady(SavedImageData imageData) {
         logSuccessOnActionsReady(imageData);
-        mImageData = imageData;
 
         AccessibilityManager accessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index e4e253e..fbcd6ba 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -148,11 +148,43 @@
     }
 
     /**
-     * Shows a silent notification with the saved screenshot and actions that can be taken with it.
+     * Shows a notification to inform the user that a screenshot is currently being saved.
+     */
+    public void showSavingScreenshotNotification() {
+        final long now = System.currentTimeMillis();
+
+        mPublicNotificationBuilder
+                .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
+                .setSmallIcon(R.drawable.stat_notify_image)
+                .setCategory(Notification.CATEGORY_PROGRESS)
+                .setWhen(now)
+                .setShowWhen(true)
+                .setColor(mResources.getColor(
+                        com.android.internal.R.color.system_notification_accent_color));
+        SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
+
+        mNotificationBuilder
+                .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
+                .setSmallIcon(R.drawable.stat_notify_image)
+                .setWhen(now)
+                .setShowWhen(true)
+                .setColor(mResources.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setStyle(mNotificationStyle)
+                .setPublicVersion(mPublicNotificationBuilder.build());
+        mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
+        SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
+
+        mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+                mNotificationBuilder.build());
+    }
+
+    /**
+     * Shows a notification with the saved screenshot and actions that can be taken with it.
      *
      * @param actionData SavedImageData struct with image URI and actions
      */
-    public void showSilentScreenshotNotification(
+    public void showScreenshotActionsNotification(
             GlobalScreenshot.SavedImageData actionData) {
         mNotificationBuilder.addAction(actionData.shareAction);
         mNotificationBuilder.addAction(actionData.editAction);
@@ -174,34 +206,20 @@
                 .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
                 .setContentText(mResources.getString(R.string.screenshot_saved_text))
                 .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
-                .setSmallIcon(R.drawable.stat_notify_image)
-                .setCategory(Notification.CATEGORY_PROGRESS)
                 .setWhen(now)
-                .setShowWhen(true)
                 .setAutoCancel(true)
                 .setColor(mContext.getColor(
-                        com.android.internal.R.color.system_notification_accent_color))
-                .setGroup("silent")
-                .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
+                        com.android.internal.R.color.system_notification_accent_color));
         mNotificationBuilder
                 .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
                 .setContentText(mResources.getString(R.string.screenshot_saved_text))
                 .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
-                .setSmallIcon(R.drawable.stat_notify_image)
-                .setCategory(Notification.CATEGORY_PROGRESS)
                 .setWhen(now)
-                .setShowWhen(true)
                 .setAutoCancel(true)
                 .setColor(mContext.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
                 .setPublicVersion(mPublicNotificationBuilder.build())
-                .setStyle(mNotificationStyle)
-                .setFlag(Notification.FLAG_NO_CLEAR, false)
-                .setGroup("silent")
-                .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
-
-        SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-        SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
+                .setFlag(Notification.FLAG_NO_CLEAR, false);
 
         mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
                 mNotificationBuilder.build());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 1669345..21810c0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -253,9 +253,7 @@
             mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
             mRotateSplitLayout = null;
         }
-        if (isSplitActive()) {
-            update(newConfig);
-        }
+        update(newConfig);
     }
 
     Handler getHandler() {
@@ -330,6 +328,11 @@
         mHandler.post(this::removeDivider);
     }
 
+    void onTasksReady() {
+        mHandler.post(() -> update(mDisplayController.getDisplayContext(
+                mContext.getDisplayId()).getResources().getConfiguration()));
+    }
+
     private void updateVisibility(final boolean visible) {
         if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
         if (mVisible != visible) {
@@ -521,7 +524,7 @@
 
     void ensureMinimizedSplit() {
         setHomeMinimized(true /* minimized */, mSplits.mSecondary.isResizable());
-        if (mView != null && !isDividerVisible()) {
+        if (!isDividerVisible()) {
             // Wasn't in split-mode yet, so enter now.
             if (DEBUG) {
                 Slog.d(TAG, " entering split mode with minimized=true");
@@ -532,7 +535,7 @@
 
     void ensureNormalSplit() {
         setHomeMinimized(false /* minimized */, mHomeStackResizable);
-        if (mView != null && !isDividerVisible()) {
+        if (!isDividerVisible()) {
             // Wasn't in split-mode, so enter now.
             if (DEBUG) {
                 Slog.d(TAG, " enter split mode unminimized ");
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 44674df..db32482 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -113,6 +113,8 @@
                 t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
                 t.apply();
                 releaseTransaction(t);
+
+                mDivider.onTasksReady();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ad04788..bd0d0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -29,6 +29,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -36,6 +37,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -58,6 +60,7 @@
  */
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
+    private static final boolean DEBUG = false;
 
     /** The minimum delay in ms between reports of notification visibility. */
     private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
@@ -79,7 +82,12 @@
     private long mLastVisibilityReportUptimeMs;
     private NotificationListContainer mListContainer;
     private final Object mDozingLock = new Object();
-    private boolean mDozing;
+    @GuardedBy("mDozingLock")
+    private Boolean mDozing = null;  // Use null to indicate state is not yet known
+    @GuardedBy("mDozingLock")
+    private Boolean mLockscreen = null;  // Use null to indicate state is not yet known
+    private Boolean mPanelExpanded = null;  // Use null to indicate state is not yet known
+    private boolean mLogging = false;
 
     protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
             new OnChildLocationsChangedListener() {
@@ -247,33 +255,44 @@
     }
 
     public void stopNotificationLogging() {
-        // Report all notifications as invisible and turn down the
-        // reporter.
-        if (!mCurrentlyVisibleNotifications.isEmpty()) {
-            logNotificationVisibilityChanges(
-                    Collections.emptyList(), mCurrentlyVisibleNotifications);
-            recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+        if (mLogging) {
+            mLogging = false;
+            if (DEBUG) {
+                Log.i(TAG, "stopNotificationLogging: log notifications invisible");
+            }
+            // Report all notifications as invisible and turn down the
+            // reporter.
+            if (!mCurrentlyVisibleNotifications.isEmpty()) {
+                logNotificationVisibilityChanges(
+                        Collections.emptyList(), mCurrentlyVisibleNotifications);
+                recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+            }
+            mHandler.removeCallbacks(mVisibilityReporter);
+            mListContainer.setChildLocationsChangedListener(null);
         }
-        mHandler.removeCallbacks(mVisibilityReporter);
-        mListContainer.setChildLocationsChangedListener(null);
     }
 
     public void startNotificationLogging() {
-        mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
-        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
-        // cause the scroller to emit child location events. Hence generate
-        // one ourselves to guarantee that we're reporting visible
-        // notifications.
-        // (Note that in cases where the scroller does emit events, this
-        // additional event doesn't break anything.)
-        mNotificationLocationsChangedListener.onChildLocationsChanged();
-        mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(),
-                mEntryManager.getVisibleNotifications());
+        if (!mLogging) {
+            mLogging = true;
+            if (DEBUG) {
+                Log.i(TAG, "startNotificationLogging");
+            }
+            mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+            // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
+            // cause the scroller to emit child location events. Hence generate
+            // one ourselves to guarantee that we're reporting visible
+            // notifications.
+            // (Note that in cases where the scroller does emit events, this
+            // additional event doesn't break anything.)
+            mNotificationLocationsChangedListener.onChildLocationsChanged();
+        }
     }
 
     private void setDozing(boolean dozing) {
         synchronized (mDozingLock) {
             mDozing = dozing;
+            maybeUpdateLoggingStatus();
         }
     }
 
@@ -343,19 +362,12 @@
                 for (int i = 0; i < N; i++) {
                     newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
                 }
-
-                synchronized (mDozingLock) {
-                    // setNotificationsShown should only be called if we are confident that
-                    // the user has seen the notification, aka not when ambient display is on
-                    if (!mDozing) {
-                        // TODO: Call NotificationEntryManager to do this, once it exists.
-                        // TODO: Consider not catching all runtime exceptions here.
-                        try {
-                            mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
-                        } catch (RuntimeException e) {
-                            Log.d(TAG, "failed setNotificationsShown: ", e);
-                        }
-                    }
+                // TODO: Call NotificationEntryManager to do this, once it exists.
+                // TODO: Consider not catching all runtime exceptions here.
+                try {
+                    mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
+                } catch (RuntimeException e) {
+                    Log.d(TAG, "failed setNotificationsShown: ", e);
                 }
             }
             recycleAllVisibilityObjects(newlyVisibleAr);
@@ -400,14 +412,64 @@
 
     @Override
     public void onStateChanged(int newState) {
-        // don't care about state change
+        if (DEBUG) {
+            Log.i(TAG, "onStateChanged: new=" + newState);
+        }
+        synchronized (mDozingLock) {
+            mLockscreen = (newState == StatusBarState.KEYGUARD
+                    || newState == StatusBarState.SHADE_LOCKED);
+        }
     }
 
     @Override
     public void onDozingChanged(boolean isDozing) {
+        if (DEBUG) {
+            Log.i(TAG, "onDozingChanged: new=" + isDozing);
+        }
         setDozing(isDozing);
     }
 
+    @GuardedBy("mDozingLock")
+    private void maybeUpdateLoggingStatus() {
+        if (mPanelExpanded == null || mDozing == null) {
+            if (DEBUG) {
+                Log.i(TAG, "Panel status unclear: panelExpandedKnown="
+                        + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null));
+            }
+            return;
+        }
+        // Once we know panelExpanded and Dozing, turn logging on & off when appropriate
+        boolean lockscreen = mLockscreen == null ? false : mLockscreen;
+        if (mPanelExpanded && !mDozing) {
+            mNotificationPanelLogger.logPanelShown(lockscreen,
+                    mEntryManager.getVisibleNotifications());
+            if (DEBUG) {
+                Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen);
+            }
+            startNotificationLogging();
+        } else {
+            if (DEBUG) {
+                Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen);
+            }
+            stopNotificationLogging();
+        }
+    }
+
+    /**
+     * Called by StatusBar to notify the logger that the panel expansion has changed.
+     * The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
+     * @param isExpanded True if the panel is expanded.
+     */
+    public void onPanelExpandedChanged(boolean isExpanded) {
+        if (DEBUG) {
+            Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
+        }
+        mPanelExpanded = isExpanded;
+        synchronized (mDozingLock) {
+            maybeUpdateLoggingStatus();
+        }
+    }
+
     /**
      * Called when the notification is expanded / collapsed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 0bdac39..c87b998 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.children
+import com.android.systemui.util.takeUntil
 import com.android.systemui.util.foldToSparseArray
 import javax.inject.Inject
 
@@ -197,7 +198,7 @@
         else -> null
     }
 
-    private fun logShadeContents() = parent.children.forEachIndexed { i, child ->
+    private fun logShadeChild(i: Int, child: View) {
         when {
             child === incomingHeaderView -> logger.logIncomingHeader(i)
             child === mediaControlsView -> logger.logMediaControls(i)
@@ -216,6 +217,7 @@
             }
         }
     }
+    private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild)
 
     private val isUsingMultipleSections: Boolean
         get() = sectionsFeatureManager.getNumberOfBuckets() > 1
@@ -223,6 +225,57 @@
     @VisibleForTesting
     fun updateSectionBoundaries() = updateSectionBoundaries("test")
 
+    private interface SectionUpdateState<out T : ExpandableView> {
+        val header: T
+        var currentPosition: Int?
+        var targetPosition: Int?
+        fun adjustViewPosition()
+    }
+
+    private fun <T : ExpandableView> expandableViewHeaderState(header: T): SectionUpdateState<T> =
+            object : SectionUpdateState<T> {
+                override val header = header
+                override var currentPosition: Int? = null
+                override var targetPosition: Int? = null
+
+                override fun adjustViewPosition() {
+                    val target = targetPosition
+                    val current = currentPosition
+                    if (target == null) {
+                        if (current != null) {
+                            parent.removeView(header)
+                        }
+                    } else {
+                        if (current == null) {
+                            // If the header is animating away, it will still have a parent, so
+                            // detach it first
+                            // TODO: We should really cancel the active animations here. This will
+                            //  happen automatically when the view's intro animation starts, but
+                            //  it's a fragile link.
+                            header.transientContainer?.removeTransientView(header)
+                            header.transientContainer = null
+                            parent.addView(header, target)
+                        } else {
+                            parent.changeViewPosition(header, target)
+                        }
+                    }
+                }
+    }
+
+    private fun <T : StackScrollerDecorView> decorViewHeaderState(
+        header: T
+    ): SectionUpdateState<T> {
+        val inner = expandableViewHeaderState(header)
+        return object : SectionUpdateState<T> by inner {
+            override fun adjustViewPosition() {
+                inner.adjustViewPosition()
+                if (targetPosition != null && currentPosition == null) {
+                    header.isContentVisible = true
+                }
+            }
+        }
+    }
+
     /**
      * Should be called whenever notifs are added, removed, or updated. Updates section boundary
      * bookkeeping and adds/moves/removes section headers if appropriate.
@@ -238,233 +291,136 @@
         // Then, once we find the start of a new section, we track that position as the "target" for
         // the section header, adjusted for the case where existing headers are in front of that
         // target, but won't be once they are moved / removed after the pass has completed.
+
         val showHeaders = statusBarStateController.state != StatusBarState.KEYGUARD
         val usingPeopleFiltering = sectionsFeatureManager.isFilteringEnabled()
         val usingMediaControls = sectionsFeatureManager.isMediaControlsEnabled()
 
+        val mediaState = mediaControlsView?.let(::expandableViewHeaderState)
+        val incomingState = incomingHeaderView?.let(::decorViewHeaderState)
+        val peopleState = peopleHeaderView?.let(::decorViewHeaderState)
+        val alertingState = alertingHeaderView?.let(::decorViewHeaderState)
+        val gentleState = silentHeaderView?.let(::decorViewHeaderState)
+
+        fun getSectionState(view: View): SectionUpdateState<ExpandableView>? = when {
+            view === mediaControlsView -> mediaState
+            view === incomingHeaderView -> incomingState
+            view === peopleHeaderView -> peopleState
+            view === alertingHeaderView -> alertingState
+            view === silentHeaderView -> gentleState
+            else -> null
+        }
+
+        val headersOrdered = sequenceOf(
+                mediaState, incomingState, peopleState, alertingState, gentleState
+        ).filterNotNull()
+
         var peopleNotifsPresent = false
-        var currentMediaControlsIdx = -1
-        val mediaControlsTarget = if (usingMediaControls) 0 else -1
-        var currentIncomingHeaderIdx = -1
-        var incomingHeaderTarget = -1
-        var currentPeopleHeaderIdx = -1
-        var peopleHeaderTarget = -1
-        var currentAlertingHeaderIdx = -1
-        var alertingHeaderTarget = -1
-        var currentGentleHeaderIdx = -1
-        var gentleHeaderTarget = -1
-
         var lastNotifIndex = 0
-        var lastIncomingIndex = -1
-        var prev: ExpandableNotificationRow? = null
+        var nextBucket: Int? = null
+        var inIncomingSection = false
 
-        for ((i, child) in parent.children.withIndex()) {
-            when {
-                // Track the existing positions of the headers
-                child === incomingHeaderView -> {
-                    logger.logIncomingHeader(i)
-                    currentIncomingHeaderIdx = i
-                }
-                child === mediaControlsView -> {
-                    logger.logMediaControls(i)
-                    currentMediaControlsIdx = i
-                }
-                child === peopleHeaderView -> {
-                    logger.logConversationsHeader(i)
-                    currentPeopleHeaderIdx = i
-                }
-                child === alertingHeaderView -> {
-                    logger.logAlertingHeader(i)
-                    currentAlertingHeaderIdx = i
-                }
-                child === silentHeaderView -> {
-                    logger.logSilentHeader(i)
-                    currentGentleHeaderIdx = i
-                }
-                child !is ExpandableNotificationRow -> logger.logOther(i, child.javaClass)
-                else -> {
-                    lastNotifIndex = i
-                    // Is there a section discontinuity? This usually occurs due to HUNs
-                    if (prev?.entry?.bucket?.let { it > child.entry.bucket } == true) {
-                        // Remove existing headers, and move the Incoming header if necessary
-                        incomingHeaderTarget = when {
-                            !showHeaders -> -1
-                            incomingHeaderTarget != -1 -> incomingHeaderTarget
-                            peopleHeaderTarget != -1 -> peopleHeaderTarget
-                            alertingHeaderTarget != -1 -> alertingHeaderTarget
-                            gentleHeaderTarget != -1 -> gentleHeaderTarget
-                            else -> 0
-                        }
-                        peopleHeaderTarget = -1
-                        alertingHeaderTarget = -1
-                        gentleHeaderTarget = -1
-                        // Walk backwards changing all previous notifications to the Incoming
-                        // section
-                        for (j in i - 1 downTo lastIncomingIndex + 1) {
-                            val prevChild = parent.getChildAt(j)
-                            if (prevChild is ExpandableNotificationRow) {
-                                prevChild.entry.bucket = BUCKET_HEADS_UP
-                            }
-                        }
-                        // Track the new bottom of the Incoming section
-                        lastIncomingIndex = i - 1
-                    }
-                    val isHeadsUp = child.isHeadsUp
-                    when (child.entry.bucket) {
-                        BUCKET_FOREGROUND_SERVICE -> logger.logForegroundService(i, isHeadsUp)
-                        BUCKET_PEOPLE -> {
-                            logger.logConversation(i, isHeadsUp)
-                            peopleNotifsPresent = true
-                            if (showHeaders && peopleHeaderTarget == -1) {
-                                peopleHeaderTarget = i
-                                // Offset the target if there are other headers before this that
-                                // will be moved.
-                                if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
-                                    peopleHeaderTarget--
-                                }
-                                if (currentPeopleHeaderIdx != -1) {
-                                    peopleHeaderTarget--
-                                }
-                                if (currentAlertingHeaderIdx != -1) {
-                                    peopleHeaderTarget--
-                                }
-                                if (currentGentleHeaderIdx != -1) {
-                                    peopleHeaderTarget--
-                                }
-                            }
-                        }
-                        BUCKET_ALERTING -> {
-                            logger.logAlerting(i, isHeadsUp)
-                            if (showHeaders && usingPeopleFiltering && alertingHeaderTarget == -1) {
-                                alertingHeaderTarget = i
-                                // Offset the target if there are other headers before this that
-                                // will be moved.
-                                if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
-                                    alertingHeaderTarget--
-                                }
-                                if (currentPeopleHeaderIdx != -1 && peopleHeaderTarget == -1) {
-                                    // People header will be removed
-                                    alertingHeaderTarget--
-                                }
-                                if (currentAlertingHeaderIdx != -1) {
-                                    alertingHeaderTarget--
-                                }
-                                if (currentGentleHeaderIdx != -1) {
-                                    alertingHeaderTarget--
-                                }
-                            }
-                        }
-                        BUCKET_SILENT -> {
-                            logger.logSilent(i, isHeadsUp)
-                            if (showHeaders && gentleHeaderTarget == -1) {
-                                gentleHeaderTarget = i
-                                // Offset the target if there are other headers before this that
-                                // will be moved.
-                                if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
-                                    gentleHeaderTarget--
-                                }
-                                if (currentPeopleHeaderIdx != -1 && peopleHeaderTarget == -1) {
-                                    // People header will be removed
-                                    gentleHeaderTarget--
-                                }
-                                if (currentAlertingHeaderIdx != -1 && alertingHeaderTarget == -1) {
-                                    // Alerting header will be removed
-                                    gentleHeaderTarget--
-                                }
-                                if (currentGentleHeaderIdx != -1) {
-                                    gentleHeaderTarget--
-                                }
-                            }
-                        }
-                    }
-
-                    prev = child
+        // Iterating backwards allows for easier construction of the Incoming section, as opposed
+        // to backtracking when a discontinuity in the sections is discovered.
+        // Iterating to -1 in order to support the case where a header is at the very top of the
+        // shade.
+        for (i in parent.childCount - 1 downTo -1) {
+            val child: View? = parent.getChildAt(i)
+            child?.let {
+                logShadeChild(i, child)
+                // If this child is a header, update the tracked positions
+                getSectionState(child)?.let { state ->
+                    state.currentPosition = i
+                    // If headers that should appear above this one in the shade already have a
+                    // target index, then we need to decrement them in order to account for this one
+                    // being either removed, or moved below them.
+                    headersOrdered.takeUntil { it === state }
+                            .forEach { it.targetPosition = it.targetPosition?.minus(1) }
                 }
             }
+
+            val row = child as? ExpandableNotificationRow
+
+            // Is there a section discontinuity? This usually occurs due to HUNs
+            inIncomingSection = inIncomingSection || nextBucket?.let { next ->
+                row?.entry?.bucket?.let { curr -> next < curr }
+            } == true
+
+            if (inIncomingSection) {
+                // Update the bucket to reflect that it's being placed in the Incoming section
+                row?.entry?.bucket = BUCKET_HEADS_UP
+            }
+
+            // Insert a header in front of the next row, if there's a boundary between it and this
+            // row, or if it is the topmost row.
+            val isSectionBoundary = nextBucket != null &&
+                    (child == null || row != null && nextBucket != row.entry.bucket)
+            if (isSectionBoundary && showHeaders) {
+                when (nextBucket) {
+                    BUCKET_HEADS_UP -> incomingState?.targetPosition = i + 1
+                    BUCKET_PEOPLE -> peopleState?.targetPosition = i + 1
+                    BUCKET_ALERTING -> alertingState?.targetPosition = i + 1
+                    BUCKET_SILENT -> gentleState?.targetPosition = i + 1
+                }
+            }
+
+            row ?: continue
+
+            // Check if there are any people notifications
+            peopleNotifsPresent = peopleNotifsPresent || row.entry.bucket == BUCKET_PEOPLE
+
+            if (nextBucket == null) {
+                lastNotifIndex = i
+            }
+            nextBucket = row.entry.bucket
         }
 
-        if (showHeaders && usingPeopleFiltering && peopleHubVisible && peopleHeaderTarget == -1) {
-            // Insert the people header even if there are no people visible, in order to show
-            // the hub. Put it directly above the next header.
-            peopleHeaderTarget = when {
-                alertingHeaderTarget != -1 -> alertingHeaderTarget
-                gentleHeaderTarget != -1 -> gentleHeaderTarget
-                else -> lastNotifIndex // Put it at the end of the list.
-            }
+        if (showHeaders && usingPeopleFiltering && peopleHubVisible) {
+            peopleState?.targetPosition = peopleState?.targetPosition
+                    // Insert the people header even if there are no people visible, in order to
+                    // show the hub. Put it directly above the next header.
+                    ?: alertingState?.targetPosition
+                    ?: gentleState?.targetPosition
+                    // Put it at the end of the list.
+                    ?: lastNotifIndex
+
             // Offset the target to account for the current position of the people header.
-            if (currentPeopleHeaderIdx != -1 && currentPeopleHeaderIdx < peopleHeaderTarget) {
-                peopleHeaderTarget--
+            peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
+                peopleState?.targetPosition?.let { target ->
+                    if (current < target) target - 1 else target
+                }
             }
         }
 
+        mediaState?.targetPosition = if (usingMediaControls) 0 else null
+
         logger.logStr("New header target positions:")
-        logger.logIncomingHeader(incomingHeaderTarget)
-        logger.logMediaControls(mediaControlsTarget)
-        logger.logConversationsHeader(peopleHeaderTarget)
-        logger.logAlertingHeader(alertingHeaderTarget)
-        logger.logSilentHeader(gentleHeaderTarget)
+        logger.logMediaControls(mediaState?.targetPosition ?: -1)
+        logger.logIncomingHeader(incomingState?.targetPosition ?: -1)
+        logger.logConversationsHeader(peopleState?.targetPosition ?: -1)
+        logger.logAlertingHeader(alertingState?.targetPosition ?: -1)
+        logger.logSilentHeader(gentleState?.targetPosition ?: -1)
 
-        // Add headers in reverse order to preserve indices
-        silentHeaderView?.let {
-            adjustHeaderVisibilityAndPosition(gentleHeaderTarget, it, currentGentleHeaderIdx)
-        }
-        alertingHeaderView?.let {
-            adjustHeaderVisibilityAndPosition(alertingHeaderTarget, it, currentAlertingHeaderIdx)
-        }
-        peopleHeaderView?.let {
-            adjustHeaderVisibilityAndPosition(peopleHeaderTarget, it, currentPeopleHeaderIdx)
-        }
-        incomingHeaderView?.let {
-            adjustHeaderVisibilityAndPosition(incomingHeaderTarget, it, currentIncomingHeaderIdx)
-        }
-        mediaControlsView?.let {
-            adjustViewPosition(mediaControlsTarget, it, currentMediaControlsIdx)
-        }
+        // Update headers in reverse order to preserve indices, otherwise movements earlier in the
+        // list will affect the target indices of the headers later in the list.
+        headersOrdered.asIterable().reversed().forEach { it.adjustViewPosition() }
 
         logger.logStr("Final order:")
         logShadeContents()
         logger.logStr("Section boundary update complete")
 
         // Update headers to reflect state of section contents
-        silentHeaderView?.setAreThereDismissableGentleNotifs(
-                parent.hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
-        )
-        peopleHeaderView?.canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
-        if (peopleHeaderTarget != currentPeopleHeaderIdx) {
-            peopleHeaderView?.resetTranslation()
+        silentHeaderView?.run {
+            val hasActiveClearableNotifications = this@NotificationSectionsManager.parent
+                    .hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
+            setAreThereDismissableGentleNotifs(hasActiveClearableNotifications)
         }
-    }
-
-    private fun adjustHeaderVisibilityAndPosition(
-        targetPosition: Int,
-        header: StackScrollerDecorView,
-        currentPosition: Int
-    ) {
-        adjustViewPosition(targetPosition, header, currentPosition)
-        if (targetPosition != -1 && currentPosition == -1) {
-            header.isContentVisible = true
-        }
-    }
-
-    private fun adjustViewPosition(
-        targetPosition: Int,
-        view: ExpandableView,
-        currentPosition: Int
-    ) {
-        if (targetPosition == -1) {
-            if (currentPosition != -1) {
-                parent.removeView(view)
-            }
-        } else {
-            if (currentPosition == -1) {
-                // If the header is animating away, it will still have a parent, so detach it first
-                // TODO: We should really cancel the active animations here. This will happen
-                // automatically when the view's intro animation starts, but it's a fragile link.
-                view.transientContainer?.removeTransientView(view)
-                view.transientContainer = null
-                parent.addView(view, targetPosition)
-            } else {
-                parent.changeViewPosition(view, targetPosition)
+        peopleHeaderView?.run {
+            canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
+            peopleState?.targetPosition?.let { targetPosition ->
+                if (targetPosition != peopleState.currentPosition) {
+                    resetTranslation()
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 825919f..fc8c8db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -19,7 +19,6 @@
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -35,7 +34,6 @@
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.util.UserAwareController;
 
 import java.util.ArrayList;
 import java.util.Objects;
@@ -45,7 +43,7 @@
 /**
  * Manages which tiles should be automatically added to QS.
  */
-public class AutoTileManager implements UserAwareController {
+public class AutoTileManager {
     private static final String TAG = "AutoTileManager";
 
     public static final String HOTSPOT = "hotspot";
@@ -54,9 +52,7 @@
     public static final String WORK = "work";
     public static final String NIGHT = "night";
     public static final String CAST = "cast";
-    static final String SETTING_SEPARATOR = ":";
-
-    private UserHandle mCurrentUser;
+    public static final String SETTING_SEPARATOR = ":";
 
     private final Context mContext;
     private final QSTileHost mHost;
@@ -70,56 +66,43 @@
     private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
 
     @Inject
-    public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
-            QSTileHost host,
+    public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
             @Background Handler handler,
             HotspotController hotspotController,
             DataSaverController dataSaverController,
             ManagedProfileController managedProfileController,
             NightDisplayListener nightDisplayListener,
             CastController castController) {
+        mAutoTracker = autoAddTracker;
         mContext = context;
         mHost = host;
-        mCurrentUser = mHost.getUserContext().getUser();
-        mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
         mHandler = handler;
         mHotspotController = hotspotController;
         mDataSaverController = dataSaverController;
         mManagedProfileController = managedProfileController;
         mNightDisplayListener = nightDisplayListener;
         mCastController = castController;
-
-        populateSettingsList();
-        startControllersAndSettingsListeners();
-    }
-
-    protected void startControllersAndSettingsListeners() {
         if (!mAutoTracker.isAdded(HOTSPOT)) {
-            mHotspotController.addCallback(mHotspotCallback);
+            hotspotController.addCallback(mHotspotCallback);
         }
         if (!mAutoTracker.isAdded(SAVER)) {
-            mDataSaverController.addCallback(mDataSaverListener);
+            dataSaverController.addCallback(mDataSaverListener);
         }
         if (!mAutoTracker.isAdded(WORK)) {
-            mManagedProfileController.addCallback(mProfileCallback);
+            managedProfileController.addCallback(mProfileCallback);
         }
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            mNightDisplayListener.setCallback(mNightDisplayCallback);
+            nightDisplayListener.setCallback(mNightDisplayCallback);
         }
         if (!mAutoTracker.isAdded(CAST)) {
-            mCastController.addCallback(mCastCallback);
+            castController.addCallback(mCastCallback);
         }
-
-        int settingsN = mAutoAddSettingList.size();
-        for (int i = 0; i < settingsN; i++) {
-            if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
-                mAutoAddSettingList.get(i).setListening(true);
-            }
-        }
+        populateSettingsList();
     }
 
-    protected void stopListening() {
+    public void destroy() {
+        mAutoTracker.destroy();
         mHotspotController.removeCallback(mHotspotCallback);
         mDataSaverController.removeCallback(mDataSaverListener);
         mManagedProfileController.removeCallback(mProfileCallback);
@@ -133,11 +116,6 @@
         }
     }
 
-    public void destroy() {
-        stopListening();
-        mAutoTracker.destroy();
-    }
-
     /**
      * Populates a list with the pairs setting:spec in the config resource.
      * <p>
@@ -159,39 +137,17 @@
             if (split.length == 2) {
                 String setting = split[0];
                 String spec = split[1];
-                // Populate all the settings. As they may not have been added in other users
-                AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
-                mAutoAddSettingList.add(s);
+                if (!mAutoTracker.isAdded(spec)) {
+                    AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
+                    mAutoAddSettingList.add(s);
+                    s.setListening(true);
+                }
             } else {
                 Log.w(TAG, "Malformed item in array: " + tile);
             }
         }
     }
 
-    @Override
-    public void changeUser(UserHandle newUser) {
-        if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
-            mHandler.post(() -> changeUser(newUser));
-            return;
-        }
-        if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
-            return;
-        }
-        stopListening();
-        mCurrentUser = newUser;
-        int settingsN = mAutoAddSettingList.size();
-        for (int i = 0; i < settingsN; i++) {
-            mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
-        }
-        mAutoTracker.changeUser(newUser);
-        startControllersAndSettingsListeners();
-    }
-
-    @Override
-    public int getCurrentUserId() {
-        return mCurrentUser.getIdentifier();
-    }
-
     public void unmarkTileAsAutoAdded(String tabSpec) {
         mAutoTracker.setTileRemoved(tabSpec);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 11022bb..df121f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -323,6 +323,20 @@
                 buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate);
             }
         }
+
+        @Override
+        public void onOverviewShown(boolean fromHome) {
+            // If the overview has fixed orientation that may change display to natural rotation,
+            // we don't want the user rotation to be reset. So after user returns to application,
+            // it can keep in the original rotation.
+            mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+        }
+
+        @Override
+        public void onToggleRecentApps() {
+            // The same case as onOverviewShown but only for 3-button navigation.
+            mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+        }
     };
 
     private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9e7bf62..e720d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1328,11 +1328,15 @@
      *
      * @param velocity unit is in px / millis
      */
-    public void stopWaitingForOpenPanelGesture(final float velocity) {
+    public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) {
         if (mExpectingSynthesizedDown) {
             mExpectingSynthesizedDown = false;
-            maybeVibrateOnOpening();
-            fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
+            if (cancel) {
+                collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+            } else {
+                maybeVibrateOnOpening();
+                fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
+            }
             onTrackingStopped(false);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index de9c745..59b10e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -74,6 +74,7 @@
     private Consumer<Integer> mRotWatcherListener;
     private boolean mListenersRegistered = false;
     private boolean mIsNavigationBarShowing;
+    private boolean mSkipOverrideUserLockPrefsOnce;
 
     private final Runnable mRemoveRotationProposal =
             () -> setRotateSuggestionButtonState(false /* visible */);
@@ -349,7 +350,20 @@
         mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_SHOWN);
     }
 
+    /**
+     * Makes {@link #shouldOverrideUserLockPrefs} always return {@code false} once. It is used to
+     * avoid losing original user rotation when display rotation is changed by entering the fixed
+     * orientation overview.
+     */
+    void setSkipOverrideUserLockPrefsOnce() {
+        mSkipOverrideUserLockPrefsOnce = true;
+    }
+
     private boolean shouldOverrideUserLockPrefs(final int rotation) {
+        if (mSkipOverrideUserLockPrefsOnce) {
+            mSkipOverrideUserLockPrefsOnce = false;
+            return false;
+        }
         // Only override user prefs when returning to the natural rotation (normally portrait).
         // Don't let apps that force landscape or 180 alter user lock.
         return rotation == NATURAL_ROTATION;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9300126..3772ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1772,6 +1772,9 @@
     }
 
     public void setPanelExpanded(boolean isExpanded) {
+        if (mPanelExpanded != isExpanded) {
+            mNotificationLogger.onPanelExpandedChanged(isExpanded);
+        }
         mPanelExpanded = isExpanded;
         updateHideIconsForBouncer(false /* animate */);
         mNotificationShadeWindowController.setPanelExpanded(isExpanded);
@@ -2090,7 +2093,7 @@
     /**
      * Called when another window is about to transfer it's input focus.
      */
-    public void onInputFocusTransfer(boolean start, float velocity) {
+    public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
         if (!mCommandQueue.panelsEnabled()) {
             return;
         }
@@ -2098,7 +2101,7 @@
         if (start) {
             mNotificationPanelViewController.startWaitingForOpenPanelGesture();
         } else {
-            mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity);
+            mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
         }
     }
 
@@ -2878,7 +2881,6 @@
     }
 
     // Visibility reporting
-
     protected void handleVisibleToUserChanged(boolean visibleToUser) {
         if (visibleToUser) {
             handleVisibleToUserChangedImpl(visibleToUser);
@@ -2900,12 +2902,12 @@
         }
     }
 
-    /**
-     * The LEDs are turned off when the notification panel is shown, even just a little bit.
-     * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
-     */
-    private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
+    // Visibility reporting
+
+    void handleVisibleToUserChangedImpl(boolean visibleToUser) {
         if (visibleToUser) {
+            /* The LEDs are turned off when the notification panel is shown, even just a little bit.
+             * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */
             boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
             boolean clearNotificationEffects =
                     !mPresenter.isPresenterFullyCollapsed() &&
@@ -3983,7 +3985,7 @@
         } else if (mIsKeyguard && !unlocking) {
             mScrimController.transitionTo(ScrimState.KEYGUARD);
         } else if (mBubbleController.isStackExpanded()) {
-            mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
+            mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED, mUnlockScrimCallback);
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index c91033e..631ea9d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -22,4 +22,14 @@
 val ViewGroup.children
     get() = sequence {
         for (i in 0 until childCount) yield(getChildAt(i))
-    }
\ No newline at end of file
+    }
+
+/** Inclusive version of [Iterable.takeWhile] */
+fun <T> Sequence<T>.takeUntil(pred: (T) -> Boolean): Sequence<T> = sequence {
+    for (x in this@takeUntil) {
+        yield(x)
+        if (pred(x)) {
+            break
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt b/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
index accb81e..1a25c84 100644
--- a/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
@@ -19,6 +19,22 @@
 import android.util.SparseArray
 
 /**
+ * Transforms a sequence of Key/Value pairs into a SparseArray.
+ *
+ * See [kotlin.collections.toMap].
+ */
+fun <T> Sequence<Pair<Int, T>>.toSparseArray(size: Int = -1): SparseArray<T> {
+    val sparseArray = when {
+        size < 0 -> SparseArray<T>()
+        else -> SparseArray<T>(size)
+    }
+    for ((i, v) in this) {
+        sparseArray.put(i, v)
+    }
+    return sparseArray
+}
+
+/**
  * Transforms an [Array] into a [SparseArray], by applying each element to [keySelector] in order to
  * generate the index at which it will be placed. If two elements produce the same index, the latter
  * replaces the former in the final result.
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 701ff5e..806d9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -37,6 +37,7 @@
 ) : ConstraintLayout(context, attrs, defStyleAttr) {
 
     private val originalGoneChildrenSet: MutableSet<Int> = mutableSetOf()
+    private val originalViewAlphas: MutableMap<Int, Float> = mutableMapOf()
     private var measureAsConstraint: Boolean = false
     private var currentState: TransitionViewState = TransitionViewState()
     private var updateScheduled = false
@@ -67,6 +68,7 @@
             if (child.visibility == GONE) {
                 originalGoneChildrenSet.add(child.id)
             }
+            originalViewAlphas[child.id] = child.alpha
         }
     }
 
@@ -198,6 +200,8 @@
             if (originalGoneChildrenSet.contains(child.id)) {
                 child.visibility = View.GONE
             }
+            // Reset the alphas, to only have the alphas present from the constraintset
+            child.alpha = originalViewAlphas[child.id] ?: 1.0f
         }
         // Let's now apply the constraintSet to get the full state
         constraintSet.applyTo(this)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index e324d84..a867825 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -18,7 +18,9 @@
 
 import static android.view.WindowInsets.Type.ime;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -110,6 +112,7 @@
 
         mKeyguardSecurityContainer.startDisappearAnimation(null);
         verify(mSecurityView).startDisappearAnimation(eq(null));
-        verify(mWindowInsetsController).hide(eq(ime()));
+        verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
+                any(), any());
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 36398a6..59f8c4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -304,10 +304,6 @@
     public void testPromoteBubble_autoExpand() throws Exception {
         mBubbleController.updateBubble(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
-                .thenReturn(mRow.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
-                .thenReturn(mRow2.getEntry());
         mBubbleController.removeBubble(
                 mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
 
@@ -335,10 +331,6 @@
         mBubbleController.updateBubble(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
                 false, /* showInShade */ true);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
-                .thenReturn(mRow.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
-                .thenReturn(mRow2.getEntry());
         mBubbleController.removeBubble(
                 mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
 
@@ -441,16 +433,15 @@
         assertTrue(mSysUiStateBubblesExpanded);
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
+        assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow2.getEntry()));
 
         // Switch which bubble is expanded
-        mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
-                mRow.getEntry().getKey()));
+        mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
         mBubbleData.setExpanded(true);
-        assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
@@ -552,27 +543,27 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow2.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow2.getEntry()));
 
         // Dismiss currently expanded
         mBubbleController.removeBubble(
-                mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+                        .getEntry().getKey(),
                 BubbleController.DISMISS_USER_GESTURE);
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
 
         // Make sure first bubble is selected
-        assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
         // Dismiss that one
         mBubbleController.removeBubble(
-                mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+                        .getEntry().getKey(),
                 BubbleController.DISMISS_USER_GESTURE);
 
         // Make sure state changes and collapse happens
@@ -848,12 +839,6 @@
                 mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
         mBubbleController.updateBubble(
                 mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
-                .thenReturn(mRow.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
-                .thenReturn(mRow2.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow3.getEntry().getKey()))
-                .thenReturn(mRow3.getEntry());
         assertEquals(mBubbleData.getBubbles().size(), 3);
 
         mBubbleData.setMaxOverflowBubbles(1);
@@ -923,8 +908,6 @@
         // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -944,8 +927,6 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
 
@@ -967,8 +948,6 @@
         // GIVEN a group summary with two (non-bubble) children and one bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index be03923..72f816f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -86,7 +86,8 @@
         final String msg = "Hello there!";
         doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
         mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -97,7 +98,8 @@
         mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
 
         // Should be big text, not the small text.
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -105,7 +107,8 @@
         doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
 
         // Media notifs don't get update messages.
-        assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertNull(BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                mEntry).message);
     }
 
     @Test
@@ -121,7 +124,7 @@
 
         // Should be the last one only.
         assertEquals("Really? I prefer them that way.",
-                BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+                BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
     }
 
     @Test
@@ -136,8 +139,11 @@
                                 "Oh, hello!", 0, "Mady").toBundle()});
 
         // Should be the last one only.
-        assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
-        assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
+        assertEquals("Oh, hello!",
+                BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
+        assertEquals("Mady",
+                BubbleViewInfoTask.extractFlyoutMessage(mContext,
+                        mEntry).senderName);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 1c70db3..58e06b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -302,8 +302,6 @@
     public void testRemoveBubble_withDismissedNotif_notInOverflow() {
         mEntryListener.onEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
-                .thenReturn(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
@@ -390,14 +388,14 @@
                 true, mRow.getEntry().getKey());
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
+        assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
 
         // Switch which bubble is expanded
         mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
         mBubbleData.setExpanded(true);
-        assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
@@ -490,27 +488,27 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
         // Last added is the one that is expanded
-        assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow2.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow2.getEntry()));
 
         // Dismiss currently expanded
         mBubbleController.removeBubble(
                 mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
+                        stackView.getExpandedBubble().getKey()).getEntry().getKey(),
                 BubbleController.DISMISS_USER_GESTURE);
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
 
         // Make sure first bubble is selected
-        assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
+        assertEquals(mRow.getEntry(),
+                mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
         // Dismiss that one
         mBubbleController.removeBubble(
                 mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
+                        stackView.getExpandedBubble().getKey()).getEntry().getKey(),
                 BubbleController.DISMISS_USER_GESTURE);
 
         // Make sure state changes and collapse happens
@@ -769,8 +767,6 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
 
@@ -789,8 +785,6 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
 
@@ -813,8 +807,6 @@
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
         mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
 
         // WHEN the summary is dismissed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
index 9b8fd11..1d02b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -32,7 +32,7 @@
 
     private val bubbles = listOf(
             BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
-            BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"),
+            BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428),
             BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
     )
     private lateinit var repository: BubblePersistentRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 76c5833..f9d611c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -37,10 +37,9 @@
     private val user0 = UserHandle.of(0)
     private val user10 = UserHandle.of(10)
 
-    private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0)
-    private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
-            "key-2", 0, 16537428, "title")
-    private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+    private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1", 120, 0)
+    private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2", 0, 16537428)
+    private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3", 120, 0)
 
     private val bubbles = listOf(bubble1, bubble2, bubble3)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
index 81687c7..4946787 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
@@ -31,17 +31,17 @@
 class BubbleXmlHelperTest : SysuiTestCase() {
 
     private val bubbles = listOf(
-            BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
-            BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"),
-            BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
+        BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
+        BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428),
+        BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
     )
 
     @Test
     fun testWriteXml() {
         val expectedEntries = """
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+            <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+            <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+            <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
         """.trimIndent()
         ByteArrayOutputStream().use {
             writeXml(it, bubbles)
@@ -54,12 +54,12 @@
     @Test
     fun testReadXml() {
         val src = """
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<bs>
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
-</bs>
+            <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+            <bs>
+            <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+            <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+            <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+            </bs>
         """.trimIndent()
         val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
         assertEquals("failed parsing bubbles from xml\n$src", bubbles, actual)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 329af2b..3c1cc23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -438,6 +438,9 @@
         GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
         assertThat(dialog).isNotNull();
         assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.VISIBLE);
+
+        // Dismiss the dialog so that it does not pollute other tests
+        mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
     }
 
     @Test
@@ -455,6 +458,9 @@
         GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
         assertThat(dialog).isNotNull();
         assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
+
+        // Dismiss the dialog so that it does not pollute other tests
+        mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
     }
 
     @Test
@@ -473,6 +479,9 @@
         GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
         assertThat(dialog).isNotNull();
         assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
+
+        // Dismiss the dialog so that it does not pollute other tests
+        mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
     }
 
     private void setupDefaultActions() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 61f5a7b..0ae9461 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -41,8 +40,6 @@
 @SmallTest
 public class AutoAddTrackerTest extends SysuiTestCase {
 
-    private static final int USER = 0;
-
     private AutoAddTracker mAutoTracker;
 
     @Before
@@ -54,7 +51,7 @@
     public void testMigration() {
         Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
         Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = new AutoAddTracker(mContext);
 
         assertTrue(mAutoTracker.isAdded(SAVER));
         assertTrue(mAutoTracker.isAdded(WORK));
@@ -71,7 +68,7 @@
 
     @Test
     public void testChangeFromBackup() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = new AutoAddTracker(mContext);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
 
@@ -85,7 +82,7 @@
 
     @Test
     public void testSetAdded() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = new AutoAddTracker(mContext);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
@@ -97,35 +94,16 @@
 
     @Test
     public void testPersist() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = new AutoAddTracker(mContext);
 
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
 
         mAutoTracker.destroy();
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = new AutoAddTracker(mContext);
 
         assertTrue(mAutoTracker.isAdded(SAVER));
 
         mAutoTracker.destroy();
     }
-
-    @Test
-    public void testIndependentUsers() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.setTileAdded(SAVER);
-
-        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
-        assertFalse(mAutoTracker.isAdded(SAVER));
-    }
-
-    @Test
-    public void testChangeUser() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.setTileAdded(SAVER);
-
-        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
-        mAutoTracker.changeUser(UserHandle.of(USER));
-        assertTrue(mAutoTracker.isAdded(SAVER));
-    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index a3a46f6..06bad80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -20,6 +20,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -42,6 +44,7 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -163,29 +166,62 @@
         mUiBgExecutor.runAllReady();
         Mockito.reset(mBarService);
 
-        mLogger.stopNotificationLogging();
+        setStateAsleep();
+        mLogger.onDozingChanged(false);  // Wake to lockscreen
+        mLogger.onDozingChanged(true);  // And go back to sleep, turning off logging
         mUiBgExecutor.runAllReady();
         // The visibility objects are recycled by NotificationLogger, so we can't use specific
         // matchers here.
         verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
     }
 
+    private void setStateAsleep() {
+        mLogger.onPanelExpandedChanged(true);
+        mLogger.onDozingChanged(true);
+        mLogger.onStateChanged(StatusBarState.KEYGUARD);
+    }
+
+    private void setStateAwake() {
+        mLogger.onPanelExpandedChanged(false);
+        mLogger.onDozingChanged(false);
+        mLogger.onStateChanged(StatusBarState.SHADE);
+    }
+
     @Test
-    public void testLogPanelShownOnLoggingStart() {
+    public void testLogPanelShownOnWake() {
         when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
-        mLogger.startNotificationLogging();
+        setStateAsleep();
+        mLogger.onDozingChanged(false);  // Wake to lockscreen
         assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
-        assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen);
+        assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
         assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
         Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
         assertEquals(TEST_PACKAGE_NAME, n.packageName);
         assertEquals(TEST_UID, n.uid);
         assertEquals(1, n.instanceId);
-        assertEquals(false, n.isGroupSummary);
+        assertFalse(n.isGroupSummary);
         assertEquals(1 + BUCKET_ALERTING, n.section);
     }
 
     @Test
+    public void testLogPanelShownOnShadePull() {
+        when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+        setStateAwake();
+        // Now expand panel
+        mLogger.onPanelExpandedChanged(true);
+        assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+        assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
+        assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+        Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+        assertEquals(TEST_PACKAGE_NAME, n.packageName);
+        assertEquals(TEST_UID, n.uid);
+        assertEquals(1, n.instanceId);
+        assertFalse(n.isGroupSummary);
+        assertEquals(1 + BUCKET_ALERTING, n.section);
+    }
+
+
+    @Test
     public void testLogPanelShownHandlesNullInstanceIds() {
         // Construct a NotificationEntry like mEntry, but with a null instance id.
         NotificationEntry entry = new NotificationEntryBuilder()
@@ -198,7 +234,8 @@
         entry.setRow(mRow);
 
         when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
-        mLogger.startNotificationLogging();
+        setStateAsleep();
+        mLogger.onDozingChanged(false);  // Wake to lockscreen
         assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
         assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
         Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index c55391a..243503d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -403,11 +403,11 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                PERSON.headsUp(),   // personHeaderTarget = 0
-                INCOMING_HEADER,    // currentIncomingHeaderIdx = 1
-                ALERTING.headsUp(), // alertingHeaderTarget = 1
-                PEOPLE_HEADER,      // currentPeopleHeaderIdx = 3
-                PERSON              //
+                PERSON.headsUp(),
+                INCOMING_HEADER,
+                ALERTING.headsUp(),
+                PEOPLE_HEADER,
+                PERSON
         );
         mSectionsManager.updateSectionBoundaries();
 
@@ -520,6 +520,70 @@
                 ChildType.GENTLE);
     }
 
+    @Test
+    public void testRemoveIncomingHeader() {
+        enablePeopleFiltering();
+        enableMediaControls();
+
+        setupMockStack(
+                MEDIA_CONTROLS,
+                INCOMING_HEADER,
+                PERSON,
+                ALERTING,
+                PEOPLE_HEADER,
+                ALERTING_HEADER,
+                ALERTING,
+                ALERTING,
+                GENTLE_HEADER,
+                GENTLE,
+                GENTLE
+        );
+
+        mSectionsManager.updateSectionBoundaries();
+
+        verifyMockStack(
+                ChildType.MEDIA_CONTROLS,
+                ChildType.PEOPLE_HEADER,
+                ChildType.PERSON,
+                ChildType.ALERTING_HEADER,
+                ChildType.ALERTING,
+                ChildType.ALERTING,
+                ChildType.ALERTING,
+                ChildType.GENTLE_HEADER,
+                ChildType.GENTLE,
+                ChildType.GENTLE
+        );
+    }
+
+    @Test
+    public void testExpandIncomingSection() {
+        enablePeopleFiltering();
+
+        setupMockStack(
+                INCOMING_HEADER,
+                PERSON,
+                ALERTING,
+                PEOPLE_HEADER,
+                ALERTING,
+                PERSON,
+                ALERTING_HEADER,
+                ALERTING
+        );
+
+        mSectionsManager.updateSectionBoundaries();
+
+        verifyMockStack(
+                ChildType.INCOMING_HEADER,
+                ChildType.HEADS_UP,
+                ChildType.HEADS_UP,
+                ChildType.HEADS_UP,
+                ChildType.PEOPLE_HEADER,
+                ChildType.PERSON,
+                ChildType.ALERTING_HEADER,
+                ChildType.ALERTING
+        );
+    }
+
     private void enablePeopleFiltering() {
         when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
     }
@@ -657,7 +721,13 @@
         final List<View> children = new ArrayList<>();
         when(mNssl.getChildCount()).thenAnswer(invocation -> children.size());
         when(mNssl.getChildAt(anyInt()))
-                .thenAnswer(invocation -> children.get(invocation.getArgument(0)));
+                .thenAnswer(invocation -> {
+                    Integer index = invocation.getArgument(0);
+                    if (index == null || index < 0 || index >= children.size()) {
+                        return null;
+                    }
+                    return children.get(index);
+                });
         when(mNssl.indexOfChild(any()))
                 .thenAnswer(invocation -> children.indexOf(invocation.getArgument(0)));
         doAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 31779cd..05cdd80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,34 +16,18 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.isNotNull;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.NightDisplayListener;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableContentResolver;
-import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -54,18 +38,14 @@
 import com.android.systemui.qs.AutoAddTracker;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.HotspotController;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -84,18 +64,9 @@
     private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
     private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
 
-    private static final int USER = 0;
-
     @Mock private QSTileHost mQsTileHost;
     @Mock private AutoAddTracker mAutoAddTracker;
     @Mock private CastController mCastController;
-    @Mock private HotspotController mHotspotController;
-    @Mock private DataSaverController mDataSaverController;
-    @Mock private ManagedProfileController mManagedProfileController;
-    @Mock private NightDisplayListener mNightDisplayListener;
-    @Mock(answer = Answers.RETURNS_SELF)
-    private AutoAddTracker.Builder mAutoAddTrackerBuilder;
-    @Mock private Context mUserContext;
 
     private AutoTileManager mAutoTileManager;
 
@@ -111,110 +82,20 @@
                 }
         );
 
-        when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
-        when(mQsTileHost.getUserContext()).thenReturn(mUserContext);
-        when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
-
-        mAutoTileManager = createAutoTileManager(new
-                MyContextWrapper(mContext));
+        mAutoTileManager = createAutoTileManager();
     }
 
-    @After
-    public void tearDown() {
-        mAutoTileManager.destroy();
-    }
-
-    private AutoTileManager createAutoTileManager(Context context) {
-        return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost,
+    private AutoTileManager createAutoTileManager() {
+        return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
                 Handler.createAsync(TestableLooper.get(this).getLooper()),
-                mHotspotController,
-                mDataSaverController,
-                mManagedProfileController,
-                mNightDisplayListener,
+                mock(HotspotController.class),
+                mock(DataSaverController.class),
+                mock(ManagedProfileController.class),
+                mock(NightDisplayListener.class),
                 mCastController);
     }
 
     @Test
-    public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
-        );
-
-        InOrder inOrderHotspot = inOrder(mHotspotController);
-        inOrderHotspot.verify(mHotspotController).removeCallback(any());
-        inOrderHotspot.verify(mHotspotController).addCallback(any());
-
-        InOrder inOrderDataSaver = inOrder(mDataSaverController);
-        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
-        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
-        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
-        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
-        InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
-        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
-        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-
-        InOrder inOrderCast = inOrder(mCastController);
-        inOrderCast.verify(mCastController).removeCallback(any());
-        inOrderCast.verify(mCastController).addCallback(any());
-
-        SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
-        assertEquals(USER + 1, setting.getCurrentUser());
-        assertTrue(setting.isListening());
-    }
-
-    @Test
-    public void testChangeUserSomeCallbacksNotAdded() throws Exception {
-        when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
-        when(mAutoAddTracker.isAdded("work")).thenReturn(true);
-        when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
-        when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
-        );
-
-        verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
-
-        InOrder inOrderHotspot = inOrder(mHotspotController);
-        inOrderHotspot.verify(mHotspotController).removeCallback(any());
-        inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
-
-        InOrder inOrderDataSaver = inOrder(mDataSaverController);
-        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
-        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
-        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
-        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
-
-        InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
-        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
-        inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-
-        InOrder inOrderCast = inOrder(mCastController);
-        inOrderCast.verify(mCastController).removeCallback(any());
-        inOrderCast.verify(mCastController, never()).addCallback(any());
-
-        SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
-        assertEquals(USER + 1, setting.getCurrentUser());
-        assertFalse(setting.isListening());
-    }
-
-    @Test
-    public void testGetCurrentUserId() throws Exception {
-        assertEquals(USER, mAutoTileManager.getCurrentUserId());
-
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 100))
-        );
-
-        assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
-    }
-
-    @Test
     public void nightTileAdded_whenActivated() {
         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             return;
@@ -332,14 +213,14 @@
     public void testEmptyArray_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, new String[0]);
-        createAutoTileManager(mContext).destroy();
+        createAutoTileManager();
     }
 
     @Test
     public void testMissingConfig_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, null);
-        createAutoTileManager(mContext).destroy();
+        createAutoTileManager();
     }
 
     // Will only notify if it's listening
@@ -350,22 +231,4 @@
             s.onChange(false);
         }
     }
-
-    class MyContextWrapper extends ContextWrapper {
-
-        private TestableContentResolver mSpiedTCR;
-
-        MyContextWrapper(TestableContext context) {
-            super(context);
-            mSpiedTCR = spy(context.getContentResolver());
-            doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(),
-                    anyInt());
-            doNothing().when(mSpiedTCR).unregisterContentObserver(any());
-        }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mSpiedTCR;
-        }
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6ccdf24..961dc15 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -22,10 +22,14 @@
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
+import static java.util.concurrent.TimeUnit.MINUTES;
+
 import android.annotation.CheckResult;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.companion.Association;
 import android.companion.AssociationRequest;
@@ -36,6 +40,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
@@ -55,6 +60,7 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.UserHandle;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
@@ -71,6 +77,7 @@
 import com.android.internal.infra.PerUser;
 import com.android.internal.infra.ServiceConnector;
 import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -112,6 +119,9 @@
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "CompanionDeviceManagerService";
 
+    private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+    private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+
     private static final String XML_TAG_ASSOCIATIONS = "associations";
     private static final String XML_TAG_ASSOCIATION = "association";
     private static final String XML_ATTR_PACKAGE = "package";
@@ -150,7 +160,6 @@
             }
         };
 
-
         registerPackageMonitor();
     }
 
@@ -195,6 +204,36 @@
         if (atmInternal != null) {
             atmInternal.setCompanionAppPackages(userHandle, companionAppPackages);
         }
+
+        BackgroundThread.getHandler().sendMessageDelayed(
+                obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
+                MINUTES.toMillis(10));
+    }
+
+    void maybeGrantAutoRevokeExemptions() {
+        PackageManager pm = getContext().getPackageManager();
+        for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+            SharedPreferences pref = getContext().getSharedPreferences(
+                    new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+                    Context.MODE_PRIVATE);
+            if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+                continue;
+            }
+
+            try {
+                Set<Association> associations = readAllAssociations(userId);
+                for (Association a : associations) {
+                    try {
+                        int uid = pm.getPackageUidAsUser(a.companionAppPackage, userId);
+                        exemptFromAutoRevoke(a.companionAppPackage, uid);
+                    } catch (PackageManager.NameNotFoundException e) {
+                        Log.w(LOG_TAG, "Unknown companion package: " + a.companionAppPackage, e);
+                    }
+                }
+            } finally {
+                pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+            }
+        }
     }
 
     @Override
@@ -469,6 +508,21 @@
                     packageInfo.applicationInfo.uid,
                     NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
         }
+
+        exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
+    }
+
+    private void exemptFromAutoRevoke(String packageName, int uid) {
+        try {
+            mAppOpsManager.setMode(
+                    AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+                    uid,
+                    packageName,
+                    AppOpsManager.MODE_IGNORED);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG,
+                    "Error while granting auto revoke exemption for " + packageName, e);
+        }
     }
 
     private static <T> boolean containsEither(T[] array, T a, T b) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a3c164d..23bf955 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -310,27 +310,30 @@
     private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates =
             new ArrayList<Map<Integer, PreciseDataConnectionState>>();
 
-    // Nothing here yet, but putting it here in case we want to add more in the future.
-    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+    static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                    | PhoneStateListener.LISTEN_BARRING_INFO;
 
     static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CELL_LOCATION
-                    | PhoneStateListener.LISTEN_CELL_INFO;
+                    | PhoneStateListener.LISTEN_CELL_INFO
+                    | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                    | PhoneStateListener.LISTEN_BARRING_INFO;
 
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
-                        | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
-                        | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
+            PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+                    | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+                    | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+                    | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
 
     static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                        | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
-                        | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
-                        | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
-                        | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
-                        | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
-                        | PhoneStateListener.LISTEN_BARRING_INFO;
+            PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                    | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+                    | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+                    | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+                    | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
+                    | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+                    | PhoneStateListener.LISTEN_BARRING_INFO;
 
     static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -338,9 +341,9 @@
 
     static final int READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT
-                | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
-                | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
-                | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
+                    | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
+                    | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
+                    | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
 
     private static final int MSG_USER_SWITCHED = 1;
     private static final int MSG_UPDATE_DEFAULT_SUB = 2;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 35936ba..be080e5 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -233,7 +233,7 @@
         public void onTwilightStateChanged(@Nullable TwilightState state) {
             synchronized (mLock) {
                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
-                    if (mCar) {
+                    if (shouldApplyAutomaticChangesImmediately()) {
                         updateLocked(0, 0);
                     } else {
                         registerScreenOffEventLocked();
@@ -1155,7 +1155,6 @@
     void updateLocked(int enableFlags, int disableFlags) {
         String action = null;
         String oldAction = null;
-        boolean originalComputedNightMode = mComputedNightMode;
         if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
             adjustStatusBarCarModeLocked();
             oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
@@ -1236,16 +1235,11 @@
             sendConfigurationAndStartDreamOrDockAppLocked(category);
         }
 
-        // reset overrides if mComputedNightMode changes
-        if (originalComputedNightMode != mComputedNightMode) {
-            resetNightModeOverrideLocked();
-        }
-
         // keep screen on when charging and in car mode
         boolean keepScreenOn = mCharging &&
                 ((mCarModeEnabled && mCarModeKeepsScreenOn &&
-                  (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
-                 (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
+                (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
+                (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
         if (keepScreenOn != mWakeLock.isHeld()) {
             if (keepScreenOn) {
                 mWakeLock.acquire();
@@ -1403,6 +1397,7 @@
             mComputedNightMode = false;
             return;
         }
+        resetNightModeOverrideLocked();
     }
 
     private boolean resetNightModeOverrideLocked() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9a2aee3..93a27f2 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -598,6 +598,10 @@
 
         UserRecord userRecord = routerRecord.mUserRecord;
         userRecord.mRouterRecords.remove(routerRecord);
+        routerRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
+                        routerRecord.mUserRecord.mHandler,
+                        routerRecord.mPackageName, /* preferredFeatures=*/ null));
         userRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
                         userRecord.mHandler));
@@ -613,7 +617,9 @@
         routerRecord.mDiscoveryPreference = discoveryRequest;
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
-                        routerRecord.mUserRecord.mHandler, routerRecord));
+                        routerRecord.mUserRecord.mHandler,
+                        routerRecord.mPackageName,
+                        routerRecord.mDiscoveryPreference.getPreferredFeatures()));
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
                         routerRecord.mUserRecord.mHandler));
@@ -1954,7 +1960,8 @@
             }
         }
 
-        private void notifyPreferredFeaturesChangedToManagers(@NonNull RouterRecord routerRecord) {
+        private void notifyPreferredFeaturesChangedToManagers(@NonNull String routerPackageName,
+                @Nullable List<String> preferredFeatures) {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
                 return;
@@ -1967,8 +1974,7 @@
             }
             for (IMediaRouter2Manager manager : managers) {
                 try {
-                    manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName,
-                            routerRecord.mDiscoveryPreference.getPreferredFeatures());
+                    manager.notifyPreferredFeaturesChanged(routerPackageName, preferredFeatures);
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to notify preferred features changed."
                             + " Manager probably died.", ex);
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java
index 3829b65..cfcf6eb 100644
--- a/services/core/java/com/android/server/notification/CalendarTracker.java
+++ b/services/core/java/com/android/server/notification/CalendarTracker.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.provider.CalendarContract.Attendees;
 import android.provider.CalendarContract.Calendars;
@@ -102,6 +103,8 @@
             while (cursor != null && cursor.moveToNext()) {
                 rt.add(cursor.getLong(0));
             }
+        } catch (SQLiteException e) {
+            Slog.w(TAG, "error querying calendar content provider", e);
         } finally {
             if (cursor != null) {
                 cursor.close();
@@ -118,11 +121,12 @@
         ContentUris.appendId(uriBuilder, time);
         ContentUris.appendId(uriBuilder, time + EVENT_CHECK_LOOKAHEAD);
         final Uri uri = uriBuilder.build();
-        final Cursor cursor = mUserContext.getContentResolver().query(uri, INSTANCE_PROJECTION,
-                null, null, INSTANCE_ORDER_BY);
+        Cursor cursor = null;
         final CheckEventResult result = new CheckEventResult();
         result.recheckAt = time + EVENT_CHECK_LOOKAHEAD;
         try {
+            cursor = mUserContext.getContentResolver().query(uri, INSTANCE_PROJECTION,
+                    null, null, INSTANCE_ORDER_BY);
             final ArraySet<Long> calendars = getCalendarsWithAccess();
             while (cursor != null && cursor.moveToNext()) {
                 final long begin = cursor.getLong(0);
@@ -183,9 +187,10 @@
             selection = null;
             selectionArgs = null;
         }
-        final Cursor cursor = mUserContext.getContentResolver().query(Attendees.CONTENT_URI,
-                ATTENDEE_PROJECTION, selection, selectionArgs, null);
+        Cursor cursor = null;
         try {
+            cursor = mUserContext.getContentResolver().query(Attendees.CONTENT_URI,
+                    ATTENDEE_PROJECTION, selection, selectionArgs, null);
             if (cursor == null || cursor.getCount() == 0) {
                 if (DEBUG) Log.d(TAG, "No attendees found");
                 return true;
@@ -205,6 +210,9 @@
                 rt |= eventMeets;
             }
             return rt;
+        } catch (SQLiteException e) {
+            Slog.w(TAG, "error querying attendees content provider", e);
+            return false;
         } finally {
             if (cursor != null) {
                 cursor.close();
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 0bd8b28..fc58968 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -133,8 +133,10 @@
     @Override
     public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
             File appLib32InstallDir) {
-        return getNativeLibraryPaths(new Abis(pkg, pkgSetting), appLib32InstallDir,
-                pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+        // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
+        // current state in PackageSetting is irrelevant.
+        return getNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
+                appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
                 pkgSetting.getPkgState().isUpdatedSystemApp());
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d3d9bc7..f5a49d2 100755
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2128,6 +2128,7 @@
                 baseApk = apk;
             }
 
+            // Validate and add Dex Metadata (.dm).
             final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
             if (dexMetadataFile != null) {
                 if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
@@ -2315,6 +2316,13 @@
             throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                     "Missing split for " + mPackageName);
         }
+
+        final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
+        if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
+            if (!baseApk.debuggable && !baseApk.profilableByShell) {
+                mIncrementalFileStorages.disableReadLogs();
+            }
+        }
     }
 
     private void resolveAndStageFile(File origFile, File targetFile)
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8ccf837..515225b 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -53,6 +53,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
@@ -1155,6 +1156,11 @@
     }
 
     private void checkStateAndResume(@NonNull PackageInstallerSession session) {
+        // Do not resume session if boot completed already
+        if (SystemProperties.getBoolean("sys.boot_completed", false)) {
+            return;
+        }
+
         if (!session.isCommitted()) {
             // Session hasn't been committed yet, ignore.
             return;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 41aa4ee..7cb59dc 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.Context;
+import android.media.IResourceManagerService;
 import android.media.tv.TvInputManager;
 import android.media.tv.tunerresourcemanager.CasSessionRequest;
 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
@@ -53,7 +54,7 @@
  *
  * @hide
  */
-public class TunerResourceManagerService extends SystemService {
+public class TunerResourceManagerService extends SystemService implements IBinder.DeathRecipient {
     private static final String TAG = "TunerResourceManagerService";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -76,6 +77,7 @@
 
     private TvInputManager mTvInputManager;
     private ActivityManager mActivityManager;
+    private IResourceManagerService mMediaResourceManager;
     private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
 
     // An internal resource request count to help generate resource handle.
@@ -102,6 +104,22 @@
         mActivityManager =
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
         mPriorityCongfig.parse();
+
+        if (mMediaResourceManager == null) {
+            IBinder mediaResourceManagerBinder = getBinderService("media.resource_manager");
+            if (mediaResourceManagerBinder == null) {
+                Slog.w(TAG, "Resource Manager Service not available.");
+                return;
+            }
+            try {
+                mediaResourceManagerBinder.linkToDeath(this, /*flags*/ 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not link to death of native resource manager service.");
+                return;
+            }
+            mMediaResourceManager = IResourceManagerService.Stub.asInterface(
+                    mediaResourceManagerBinder);
+        }
     }
 
     private final class BinderService extends ITunerResourceManager.Stub {
@@ -380,6 +398,19 @@
         }
     }
 
+    /**
+     * Handle the death of the native resource manager service
+     */
+    @Override
+    public void binderDied() {
+        if (DEBUG) {
+            Slog.w(TAG, "Native media resource manager service has died");
+        }
+        synchronized (mLock) {
+            mMediaResourceManager = null;
+        }
+    }
+
     @VisibleForTesting
     protected void registerClientProfileInternal(ResourceClientProfile profile,
             IResourcesReclaimListener listener, int[] clientId) {
@@ -399,6 +430,16 @@
                 ? Binder.getCallingPid() /*callingPid*/
                 : mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
 
+        // Update Media Resource Manager with the tvAppId
+        if (profile.getTvInputSessionId() != null && mMediaResourceManager != null) {
+            try {
+                mMediaResourceManager.overridePid(Binder.getCallingPid(), pid);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Could not overridePid in resourceManagerSercice,"
+                        + " remote exception: " + e);
+            }
+        }
+
         ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
                                               .tvInputSessionId(profile.getTvInputSessionId())
                                               .useCase(profile.getUseCase())
@@ -415,6 +456,15 @@
             Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
         }
         removeClientProfile(clientId);
+        // Remove the Media Resource Manager callingPid to tvAppId mapping
+        if (mMediaResourceManager != null) {
+            try {
+                mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
+                        + " remote exception: " + e);
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a072175..74ea712 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2207,10 +2207,13 @@
 
     @Override
     boolean isFocusable() {
+        return super.isFocusable() && (canReceiveKeys() || isAlwaysFocusable());
+    }
+
+    boolean canReceiveKeys() {
         // TODO(156521483): Propagate the state down the hierarchy instead of checking the parent
-        boolean canReceiveKeys = getWindowConfiguration().canReceiveKeys()
-                && getTask().getWindowConfiguration().canReceiveKeys();
-        return super.isFocusable() && (canReceiveKeys || isAlwaysFocusable());
+        return getWindowConfiguration().canReceiveKeys()
+                && (task == null || task.getWindowConfiguration().canReceiveKeys());
     }
 
     boolean isResizeable() {
@@ -2379,10 +2382,10 @@
                 // For the apps below Q, there can be only one app which has the focused window per
                 // process, because legacy apps may not be ready for a multi-focus system.
                 return false;
+
             }
         }
-        return (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable())
-                && getDisplay() != null;
+        return (canReceiveKeys() || isAlwaysFocusable()) && getDisplay() != null;
     }
 
     /**
@@ -3364,6 +3367,13 @@
 
             final long origId = Binder.clearCallingIdentity();
             try {
+                // Link the fixed rotation transform to this activity since we are transferring the
+                // starting window.
+                if (fromActivity.hasFixedRotationTransform()) {
+                    mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(this,
+                            false /* checkOpening */);
+                }
+
                 // Transfer the starting window over to the new token.
                 mStartingData = fromActivity.mStartingData;
                 startingSurface = fromActivity.startingSurface;
@@ -7744,24 +7754,25 @@
                     outAppBounds.set(outBounds);
                 }
             } else {
-                outBounds.set(0, 0, mWidth, mHeight);
-                getFrameByOrientation(outAppBounds, orientation);
-                if (orientationRequested && !canChangeOrientation
-                        && (outAppBounds.width() > outAppBounds.height()) != (mWidth > mHeight)) {
-                    // The orientation is mismatched but the display cannot rotate. The bounds will
-                    // fit to the short side of display.
-                    if (orientation == ORIENTATION_LANDSCAPE) {
-                        outAppBounds.bottom = (int) ((float) mWidth * mWidth / mHeight);
-                        outAppBounds.right = mWidth;
-                    } else {
-                        outAppBounds.bottom = mHeight;
-                        outAppBounds.right = (int) ((float) mHeight * mHeight / mWidth);
+                if (orientationRequested) {
+                    getFrameByOrientation(outBounds, orientation);
+                    if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) {
+                        // The orientation is mismatched but the display cannot rotate. The bounds
+                        // will fit to the short side of display.
+                        if (orientation == ORIENTATION_LANDSCAPE) {
+                            outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight);
+                            outBounds.right = mWidth;
+                        } else {
+                            outBounds.bottom = mHeight;
+                            outBounds.right = (int) ((float) mHeight * mHeight / mWidth);
+                        }
+                        outBounds.offset(
+                                getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
                     }
-                    outAppBounds.offset(getHorizontalCenterOffset(outBounds.width(),
-                            outAppBounds.width()), 0 /* dy */);
                 } else {
-                    outAppBounds.set(outBounds);
+                    outBounds.set(0, 0, mWidth, mHeight);
                 }
+                outAppBounds.set(outBounds);
             }
 
             if (rotation != ROTATION_UNDEFINED) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 7fd2156..34e8169 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -821,7 +821,7 @@
     /** Resume next focusable stack after reparenting to another display. */
     void postReparent() {
         adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
-                true /* moveParentsToTop */);
+                true /* moveDisplayToTop */);
         mRootWindowContainer.resumeFocusedStacksTopActivities();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 8b14095..26e0790 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -59,6 +59,7 @@
     private final int mTransparentFlag;
     private final int mStatusBarManagerId;
     private final int mTranslucentWmFlag;
+    private final int mWindowType;
     protected final Handler mHandler;
     private final Object mServiceAquireLock = new Object();
     private StatusBarManagerInternal mStatusBarInternal;
@@ -77,13 +78,14 @@
     private OnBarVisibilityChangedListener mVisibilityChangeListener;
 
     BarController(String tag, int displayId, int transientFlag, int unhideFlag, int translucentFlag,
-            int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
+            int statusBarManagerId, int windowType, int translucentWmFlag, int transparentFlag) {
         mTag = "BarController." + tag;
         mDisplayId = displayId;
         mTransientFlag = transientFlag;
         mUnhideFlag = unhideFlag;
         mTranslucentFlag = translucentFlag;
         mStatusBarManagerId = statusBarManagerId;
+        mWindowType = windowType;
         mTranslucentWmFlag = translucentWmFlag;
         mTransparentFlag = transparentFlag;
         mHandler = new BarHandler();
@@ -168,7 +170,12 @@
     }
 
     boolean isTransparentAllowed(WindowState win) {
-        return win == null || win.letterboxNotIntersectsOrFullyContains(mContentFrame);
+        if (win == null) {
+            return true;
+        }
+        final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
+        final Rect contentFrame = rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
+        return win.letterboxNotIntersectsOrFullyContains(contentFrame);
     }
 
     boolean setBarShowingLw(final boolean show) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4e19a52..fb60257 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5647,8 +5647,12 @@
             }
 
             if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) {
-                // Because it won't affect display orientation, just finish the transform.
-                animatingRecents.finishFixedRotationTransform();
+                // The recents activity should be going to be invisible (switch to another app or
+                // return to original top). Only clear the top launching record without finishing
+                // the transform immediately because it won't affect display orientation. And before
+                // the visibility is committed, the recents activity may perform relayout which may
+                // cause unexpected configuration change if the rotated configuration is restored.
+                // The transform will be finished when the transition is done.
                 setFixedRotationLaunchingAppUnchecked(null);
             } else {
                 // If there is already a launching activity that is not the recents, before its
@@ -5671,7 +5675,11 @@
             }
             if (mFixedRotationLaunchingApp != null
                     && mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
-                continueUpdateOrientationForDiffOrienLaunchingApp();
+                // Waiting until all of the associated activities have done animation, or the
+                // orientation would be updated too early and cause flickers.
+                if (!mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
+                    continueUpdateOrientationForDiffOrienLaunchingApp();
+                }
             } else {
                 r.finishFixedRotationTransform();
             }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0838d79..01680b2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -156,6 +156,7 @@
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.InputChannel;
@@ -204,6 +205,7 @@
 import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * The policy that provides the basic behaviors and states of a display to show UI.
@@ -520,6 +522,7 @@
                 View.NAVIGATION_BAR_UNHIDE,
                 View.NAVIGATION_BAR_TRANSLUCENT,
                 StatusBarManager.WINDOW_NAVIGATION_BAR,
+                TYPE_NAVIGATION_BAR,
                 FLAG_TRANSLUCENT_NAVIGATION,
                 View.NAVIGATION_BAR_TRANSPARENT);
 
@@ -1317,6 +1320,11 @@
                 displayFrames.mDisplayCutoutSafe.top);
     }
 
+    @VisibleForTesting
+    StatusBarController getStatusBarController() {
+        return mStatusBarController;
+    }
+
     WindowState getStatusBar() {
         return mStatusBar;
     }
@@ -1615,13 +1623,16 @@
     }
 
     private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames,
-            InsetsState insetsState, WindowFrames simulatedWindowFrames, Runnable layout) {
+            InsetsState insetsState, WindowFrames simulatedWindowFrames,
+            SparseArray<Rect> contentFrames, Consumer<Rect> layout) {
         win.setSimulatedWindowFrames(simulatedWindowFrames);
+        final Rect contentFrame = new Rect();
         try {
-            layout.run();
+            layout.accept(contentFrame);
         } finally {
             win.setSimulatedWindowFrames(null);
         }
+        contentFrames.put(win.mAttrs.type, contentFrame);
         mDisplayContent.getInsetsStateController().computeSimulatedState(insetsState, win,
                 displayFrames, simulatedWindowFrames);
     }
@@ -1633,24 +1644,25 @@
      * state and some temporal states. In other words, it doesn't change the window frames used to
      * show on screen.
      */
-    void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState, int uiMode) {
+    void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState,
+            SparseArray<Rect> barContentFrames) {
         displayFrames.onBeginLayout();
         updateInsetsStateForDisplayCutout(displayFrames, insetsState);
         insetsState.setDisplayFrame(displayFrames.mUnrestricted);
         final WindowFrames simulatedWindowFrames = new WindowFrames();
         if (mNavigationBar != null) {
-            simulateLayoutDecorWindow(
-                    mNavigationBar, displayFrames, insetsState, simulatedWindowFrames,
-                    () -> layoutNavigationBar(displayFrames, uiMode, mLastNavVisible,
+            simulateLayoutDecorWindow(mNavigationBar, displayFrames, insetsState,
+                    simulatedWindowFrames, barContentFrames,
+                    contentFrame -> layoutNavigationBar(displayFrames,
+                            mDisplayContent.getConfiguration().uiMode, mLastNavVisible,
                             mLastNavTranslucent, mLastNavAllowedHidden,
-                            mLastNotificationShadeForcesShowingNavigation,
-                            false /* isRealLayout */));
+                            mLastNotificationShadeForcesShowingNavigation, contentFrame));
         }
         if (mStatusBar != null) {
-            simulateLayoutDecorWindow(
-                    mStatusBar, displayFrames, insetsState, simulatedWindowFrames,
-                    () -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
-                            false /* isRealLayout */));
+            simulateLayoutDecorWindow(mStatusBar, displayFrames, insetsState,
+                    simulatedWindowFrames, barContentFrames,
+                    contentFrame -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
+                            contentFrame));
         }
         layoutScreenDecorWindows(displayFrames, simulatedWindowFrames);
         postAdjustDisplayFrames(displayFrames);
@@ -1702,9 +1714,10 @@
 
         boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
                 navTranslucent, navAllowedHidden, notificationShadeForcesShowingNavigation,
-                true /* isRealLayout */);
+                null /* simulatedContentFrame */);
         if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
-        updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, true /* isRealLayout */);
+        updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui,
+                null /* simulatedContentFrame */);
         if (updateSysUiVisibility) {
             updateSystemUiVisibilityLw();
         }
@@ -1725,10 +1738,9 @@
                 navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null;
         final InsetsState requestedState = navControllingWin != null
                 ? navControllingWin.getRequestedInsetsState() : null;
-        final InsetsSource navSource = requestedState != null
-                ? requestedState.peekSource(ITYPE_NAVIGATION_BAR) : null;
-        final boolean navVisible = navSource != null
-                ? navSource.isVisible() : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
+        final boolean navVisible = requestedState != null
+                ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
         final boolean showBarsByTouch = navControllingWin != null
                 && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH;
         // When the navigation bar isn't visible, we put up a fake input window to catch all
@@ -1877,7 +1889,8 @@
         displayFrames.mContent.set(dockFrame);
     }
 
-    private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, boolean isRealLayout) {
+    private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+            Rect simulatedContentFrame) {
         // decide where the status bar goes ahead of time
         if (mStatusBar == null) {
             return false;
@@ -1900,12 +1913,14 @@
         displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
                 displayFrames.mDisplayCutoutSafe.top);
 
-        if (isRealLayout) {
-            // Tell the bar controller where the collapsed status bar content is.
-            sTmpRect.set(windowFrames.mContentFrame);
-            sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
-            sTmpRect.top = windowFrames.mContentFrame.top; // Ignore top display cutout inset
-            sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+        // Tell the bar controller where the collapsed status bar content is.
+        sTmpRect.set(windowFrames.mContentFrame);
+        sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+        sTmpRect.top = windowFrames.mContentFrame.top; // Ignore top display cutout inset
+        sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+        if (simulatedContentFrame != null) {
+            simulatedContentFrame.set(sTmpRect);
+        } else {
             mStatusBarController.setContentFrame(sTmpRect);
         }
 
@@ -1942,7 +1957,7 @@
 
     private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
             boolean navTranslucent, boolean navAllowedHidden,
-            boolean statusBarForcesShowingNavigation, boolean isRealLayout) {
+            boolean statusBarForcesShowingNavigation, Rect simulatedContentFrame) {
         if (mNavigationBar == null) {
             return false;
         }
@@ -2046,7 +2061,9 @@
                 navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
                 navigationFrame /* stableFrame */);
         mNavigationBar.computeFrame(displayFrames);
-        if (isRealLayout) {
+        if (simulatedContentFrame != null) {
+            simulatedContentFrame.set(windowFrames.mContentFrame);
+        } else {
             mNavigationBarPosition = navBarPosition;
             mNavigationBarController.setContentFrame(windowFrames.mContentFrame);
         }
@@ -2518,12 +2535,13 @@
         final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
                 || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
                 || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
-                        && !win.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+                        && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+                                ITYPE_STATUS_BAR));
         final boolean requestedHideNavigation =
                 (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
                 || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
-                        && !win.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
-                                .isVisible());
+                        && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+                                ITYPE_NAVIGATION_BAR));
 
         // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
         // cropped / shifted to the displayFrame in WindowState.
@@ -3333,24 +3351,32 @@
             return;
         }
         if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-            if (swipeTarget == mNavigationBar
-                    && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
-                // Don't show status bar when swiping on already visible navigation bar
-                return;
-            }
             final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
             final InsetsControlTarget controlTarget = provider != null
                     ? provider.getControlTarget() : null;
 
-            // No transient mode on lockscreen (in notification shade window).
             if (controlTarget == null || controlTarget == getNotificationShade()) {
+                // No transient mode on lockscreen (in notification shade window).
                 return;
             }
+
+            if (swipeTarget == mNavigationBar
+                    && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
+                // Don't show status bar when swiping on already visible navigation bar.
+                // But restore the position of navigation bar if it has been moved by the control
+                // target.
+                controlTarget.showInsets(Type.navigationBars(), false);
+                return;
+            }
+
+            int insetsTypesToShow = Type.systemBars();
+
             if (controlTarget.canShowTransient()) {
-                mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+                insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
                         new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
-            } else {
-                controlTarget.showInsets(Type.systemBars(), false);
+            }
+            if (insetsTypesToShow != 0) {
+                controlTarget.showInsets(insetsTypesToShow, false);
             }
         } else {
             boolean sb = mStatusBarController.checkShowTransientBarLw();
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 035f201..be6e4b7 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -42,6 +42,7 @@
 import android.view.SurfaceControl;
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.ViewRootImpl;
+import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimation.Bounds;
 import android.view.WindowInsetsAnimationControlListener;
@@ -127,14 +128,16 @@
         return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
     }
 
-    void showTransient(IntArray types) {
+    @InsetsType int showTransient(IntArray types) {
+        @InsetsType int showingTransientTypes = 0;
         boolean changed = false;
         for (int i = types.size() - 1; i >= 0; i--) {
             final int type = types.get(i);
-            if (mShowingTransientTypes.indexOf(type) != -1) {
+            if (!isHidden(type)) {
                 continue;
             }
-            if (!isHidden(type)) {
+            showingTransientTypes |= InsetsState.toPublicType(type);
+            if (mShowingTransientTypes.indexOf(type) != -1) {
                 continue;
             }
             mShowingTransientTypes.add(type);
@@ -161,6 +164,7 @@
                 }
             });
         }
+        return showingTransientTypes;
     }
 
     void hideTransient() {
@@ -192,18 +196,6 @@
             state = new InsetsState(state);
             state.setSourceVisible(mShowingTransientTypes.get(i), false);
         }
-        if (mFocusedWin != null && getStatusControlTarget(mFocusedWin) == mDummyControlTarget) {
-            if (state == originalState) {
-                state = new InsetsState(state);
-            }
-            state.setSourceVisible(ITYPE_STATUS_BAR, mFocusedWin.getRequestedInsetsState());
-        }
-        if (mFocusedWin != null && getNavControlTarget(mFocusedWin) == mDummyControlTarget) {
-            if (state == originalState) {
-                state = new InsetsState(state);
-            }
-            state.setSourceVisible(ITYPE_NAVIGATION_BAR, mFocusedWin.getRequestedInsetsState());
-        }
         return state;
     }
 
@@ -373,7 +365,7 @@
             final WindowState controllingWin =
                     controlTarget instanceof WindowState ? (WindowState) controlTarget : null;
             setVisible(controllingWin == null
-                    || controllingWin.getRequestedInsetsState().getSource(type).isVisible());
+                    || controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type));
         }
 
         private void setVisible(boolean visible) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index bded651..1780820 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -233,6 +233,8 @@
             // duration of the gesture that is driven by the recents component
             targetActivity.mLaunchTaskBehind = true;
             mLaunchedTargetActivity = targetActivity;
+            // TODO(b/156772625): Evaluate to send new intents vs. replacing the intent extras.
+            targetActivity.intent.replaceExtras(mTargetIntent);
 
             // Fetch all the surface controls and pass them to the client to get the animation
             // started. Cancel any existing recents animation running synchronously (do not hold the
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
index cac992a..3564e0b 100644
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ b/services/core/java/com/android/server/wm/StatusBarController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 
@@ -90,6 +91,7 @@
                 View.STATUS_BAR_UNHIDE,
                 View.STATUS_BAR_TRANSLUCENT,
                 StatusBarManager.WINDOW_STATUS_BAR,
+                TYPE_STATUS_BAR,
                 FLAG_TRANSLUCENT_STATUS,
                 View.STATUS_BAR_TRANSPARENT);
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 56ad964..c749125 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2638,7 +2638,7 @@
      */
     ActivityStack adjustFocusToNextFocusableTask(String reason) {
         return adjustFocusToNextFocusableTask(reason, false /* allowFocusSelf */,
-                true /* moveParentsToTop */);
+                true /* moveDisplayToTop */);
     }
 
     /** Return the next focusable task by looking from the siblings and parent tasks */
@@ -2661,11 +2661,11 @@
      * Find next proper focusable task and make it focused.
      * @param reason The reason of making the adjustment.
      * @param allowFocusSelf Is the focus allowed to remain on the same task.
-     * @param moveParentsToTop Whether to move parents to top while making the task focused.
+     * @param moveDisplayToTop Whether to move display to top while making the task focused.
      * @return The root task that now got the focus, {@code null} if none found.
      */
     ActivityStack adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf,
-            boolean moveParentsToTop) {
+            boolean moveDisplayToTop) {
         ActivityStack focusableTask = (ActivityStack) getNextFocusableTask(allowFocusSelf);
         if (focusableTask == null) {
             focusableTask = mRootWindowContainer.getNextFocusableStack((ActivityStack) this,
@@ -2676,10 +2676,17 @@
         }
 
         final ActivityStack rootTask = (ActivityStack) focusableTask.getRootTask();
-        if (!moveParentsToTop) {
-            // Only move the next stack to top in its task container.
+        if (!moveDisplayToTop) {
+            // There may be multiple task layers above this task, so when relocating the task to the
+            // top, we should move this task and each of its parent task that below display area to
+            // the top of each layer.
             WindowContainer parent = focusableTask.getParent();
-            parent.positionChildAt(POSITION_TOP, focusableTask, false /* includingParents */);
+            WindowContainer next = focusableTask;
+            do {
+                parent.positionChildAt(POSITION_TOP, next, false /* includingParents */);
+                next = parent;
+                parent = next.getParent();
+            } while (next.asTask() != null && parent != null);
             return rootTask;
         }
 
@@ -2917,7 +2924,12 @@
     }
 
     boolean cropWindowsToStackBounds() {
-        return isResizeable();
+        // Don't crop HOME/RECENTS windows to stack bounds. This is because in split-screen
+        // they extend past their stack and sysui uses the stack surface to control cropping.
+        // TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
+        final boolean isTopHomeOrRecents = (isActivityTypeHome() || isActivityTypeRecents())
+                && getRootTask().getTopMostTask() == this;
+        return isResizeable() && !isTopHomeOrRecents;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9c9c61a..2e1e094 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -102,6 +103,9 @@
     private ActivityStack mRootPinnedTask;
     private ActivityStack mRootSplitScreenPrimaryTask;
 
+    // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
+    private ActivityStack mRootRecentsTask;
+
     private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
     private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
     private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
@@ -172,6 +176,8 @@
     ActivityStack getStack(int windowingMode, int activityType) {
         if (activityType == ACTIVITY_TYPE_HOME) {
             return mRootHomeTask;
+        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+            return mRootRecentsTask;
         }
         if (windowingMode == WINDOWING_MODE_PINNED) {
             return mRootPinnedTask;
@@ -208,6 +214,10 @@
         return mRootHomeTask;
     }
 
+    @Nullable ActivityStack getRootRecentsTask() {
+        return mRootRecentsTask;
+    }
+
     ActivityStack getRootPinnedTask() {
         return mRootPinnedTask;
     }
@@ -216,6 +226,15 @@
         return mRootSplitScreenPrimaryTask;
     }
 
+    ActivityStack getRootSplitScreenSecondaryTask() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).inSplitScreenSecondaryWindowingMode()) {
+                return mChildren.get(i);
+            }
+        }
+        return null;
+    }
+
     ArrayList<Task> getVisibleTasks() {
         final ArrayList<Task> visibleTasks = new ArrayList<>();
         forAllTasks(task -> {
@@ -246,6 +265,16 @@
             } else {
                 mRootHomeTask = stack;
             }
+        } else if (stack.isActivityTypeRecents()) {
+            if (mRootRecentsTask != null) {
+                if (!stack.isDescendantOf(mRootRecentsTask)) {
+                    throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
+                            + mRootRecentsTask + " already exist on display=" + this
+                            + " stack=" + stack);
+                }
+            } else {
+                mRootRecentsTask = stack;
+            }
         }
 
         if (!stack.isRootTask()) {
@@ -273,6 +302,8 @@
     void removeStackReferenceIfNeeded(ActivityStack stack) {
         if (stack == mRootHomeTask) {
             mRootHomeTask = null;
+        } else if (stack == mRootRecentsTask) {
+            mRootRecentsTask = null;
         } else if (stack == mRootPinnedTask) {
             mRootPinnedTask = null;
         } else if (stack == mRootSplitScreenPrimaryTask) {
@@ -308,8 +339,17 @@
 
     @Override
     void positionChildAt(int position, ActivityStack child, boolean includingParents) {
-        final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
+        final boolean moveToTop = position >= getChildCount() - 1;
         final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
+
+        // Reset mPreferredTopFocusableStack before positioning to top or {@link
+        // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+        // resumed activity.
+        final boolean wasContained = mChildren.contains(child);
+        if (moveToTop && wasContained && child.isFocusable()) {
+            mPreferredTopFocusableStack = null;
+        }
+
         if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
             // This stack is always-on-top, override the default behavior.
             Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
@@ -339,6 +379,17 @@
         child.updateTaskMovement(moveToTop);
 
         mDisplayContent.setLayoutNeeded();
+
+        // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+        // the original position is preferred to be top, the stack should have higher priority when
+        // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+        // preferred stack is set only when moving an existing stack to top instead of adding a new
+        // stack that may be too early (e.g. in the middle of launching or reparenting).
+        if (moveToTop && child.isFocusableAndVisible()) {
+            mPreferredTopFocusableStack = child;
+        } else if (mPreferredTopFocusableStack == child) {
+            mPreferredTopFocusableStack = null;
+        }
     }
 
     /**
@@ -736,29 +787,10 @@
                     "positionStackAt: Can only have one task on display=" + this);
         }
 
-        final boolean movingToTop = wasContained && position >= getStackCount() - 1;
-        // Reset mPreferredTopFocusableStack before positioning to top or {@link
-        // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
-        // resumed activity.
-        if (movingToTop && stack.isFocusable()) {
-            mPreferredTopFocusableStack = null;
-        }
-
         // Since positionChildAt() is called during the creation process of pinned stacks,
         // ActivityStack#getStack() can be null.
         positionStackAt(position, stack, includingParents);
 
-        // The insert position may be adjusted to non-top when there is always-on-top stack. Since
-        // the original position is preferred to be top, the stack should have higher priority when
-        // we are looking for top focusable stack. The condition {@code wasContained} restricts the
-        // preferred stack is set only when moving an existing stack to top instead of adding a new
-        // stack that may be too early (e.g. in the middle of launching or reparenting).
-        if (movingToTop && stack.isFocusableAndVisible()) {
-            mPreferredTopFocusableStack = stack;
-        } else if (mPreferredTopFocusableStack == stack) {
-            mPreferredTopFocusableStack = null;
-        }
-
         if (updateLastFocusedStackReason != null) {
             final ActivityStack currentFocusedStack = getFocusedStack();
             if (currentFocusedStack != prevFocusedStack) {
@@ -1797,21 +1829,23 @@
         // reparenting stack finished.
         // Keep the order from bottom to top.
         int numStacks = getStackCount();
+
+        final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated();
+        final ActivityStack rootStack = splitScreenActivated ? toDisplayArea
+                .getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null;
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
             final ActivityStack stack = getStackAt(stackNdx);
             // Always finish non-standard type stacks.
             if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
                 stack.finishAllActivitiesImmediately();
             } else {
-                // If default display is in split-window mode, set windowing mode of the
-                // stack to split-screen secondary. Otherwise, set the windowing mode to
-                // undefined by default to let stack inherited the windowing mode from the
-                // new display.
-                final int windowingMode = toDisplayArea.isSplitScreenModeActivated()
-                        ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
-                        : WINDOWING_MODE_UNDEFINED;
-                stack.reparent(toDisplayArea, true /* onTop */);
-                stack.setWindowingMode(windowingMode);
+                // Reparent the stack to the root task of secondary-split-screen or display area.
+                stack.reparent(stack.supportsSplitScreenWindowingMode() && rootStack != null
+                        ? rootStack : toDisplayArea, POSITION_TOP);
+
+                // Set the windowing mode to undefined by default to let the stack inherited the
+                // windowing mode.
+                stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
                 lastReparentedStack = stack;
             }
             // Stacks may be removed from this display. Ensure each stack will be processed
@@ -1819,6 +1853,17 @@
             stackNdx -= numStacks - getStackCount();
             numStacks = getStackCount();
         }
+        if (lastReparentedStack != null && splitScreenActivated) {
+            if (!lastReparentedStack.supportsSplitScreenWindowingMode()) {
+                mAtmService.getTaskChangeNotificationController()
+                        .notifyActivityDismissingDockedStack();
+                toDisplayArea.onSplitScreenModeDismissed(lastReparentedStack);
+            } else if (rootStack != null) {
+                // update focus
+                rootStack.moveToFront("display-removed");
+            }
+        }
+
         mRemoved = true;
 
         return lastReparentedStack;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 32717d0..6bbc019 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1529,6 +1529,29 @@
     }
 
     /**
+     * This is a form of rectangle "difference". It cut off each dimension of rect by the amount
+     * that toRemove is "pushing into" it from the outside. Any dimension that fully contains
+     * toRemove won't change.
+     */
+    private void cutRect(Rect rect, Rect toRemove) {
+        if (toRemove.isEmpty()) return;
+        if (toRemove.top < rect.bottom && toRemove.bottom > rect.top) {
+            if (toRemove.right >= rect.right && toRemove.left >= rect.left) {
+                rect.right = toRemove.left;
+            } else if (toRemove.left <= rect.left && toRemove.right <= rect.right) {
+                rect.left = toRemove.right;
+            }
+        }
+        if (toRemove.left < rect.right && toRemove.right > rect.left) {
+            if (toRemove.bottom >= rect.bottom && toRemove.top >= rect.top) {
+                rect.bottom = toRemove.top;
+            } else if (toRemove.top <= rect.top && toRemove.bottom <= rect.bottom) {
+                rect.top = toRemove.bottom;
+            }
+        }
+    }
+
+    /**
      * Retrieves the visible bounds of the window.
      * @param bounds The rect which gets the bounds.
      */
@@ -1544,6 +1567,20 @@
             } else {
                 intersectWithStackBounds = false;
             }
+            if (inSplitScreenPrimaryWindowingMode()) {
+                // If this is in the primary split and the home stack is the top visible task in
+                // the secondary split, it means this is "minimized" and thus must prevent
+                // overlapping with home.
+                // TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
+                final ActivityStack rootSecondary =
+                        task.getDisplayArea().getRootSplitScreenSecondaryTask();
+                if (rootSecondary.isActivityTypeHome() || rootSecondary.isActivityTypeRecents()) {
+                    final WindowContainer topTask = rootSecondary.getTopChild();
+                    if (topTask.isVisible()) {
+                        cutRect(mTmpRect, topTask.getBounds());
+                    }
+                }
+            }
         }
 
         bounds.set(mWindowFrames.mVisibleFrame);
@@ -3576,6 +3613,9 @@
     @Override
     public void notifyInsetsControlChanged() {
         ProtoLog.d(WM_DEBUG_IME, "notifyInsetsControlChanged for %s ", this);
+        if (mAppDied || mRemoved) {
+            return;
+        }
         final InsetsStateController stateController =
                 getDisplayContent().getInsetsStateController();
         try {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d257002..86aacf3 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -48,6 +48,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayAdjustments.FixedRotationAdjustments;
 import android.view.DisplayInfo;
@@ -124,7 +125,7 @@
     private static class FixedRotationTransformState {
         final DisplayInfo mDisplayInfo;
         final DisplayFrames mDisplayFrames;
-        final InsetsState mInsetsState;
+        final InsetsState mInsetsState = new InsetsState();
         final Configuration mRotatedOverrideConfiguration;
         final SeamlessRotator mRotator;
         /**
@@ -133,14 +134,14 @@
          */
         final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3);
         final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
+        final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
         boolean mIsTransforming = true;
 
         FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
-                DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState,
-                Configuration rotatedConfig, int currentRotation) {
+                DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig,
+                int currentRotation) {
             mDisplayInfo = rotatedDisplayInfo;
             mDisplayFrames = rotatedDisplayFrames;
-            mInsetsState = rotatedInsetsState;
             mRotatedOverrideConfiguration = rotatedConfig;
             // This will use unrotate as rotate, so the new and old rotation are inverted.
             mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation,
@@ -516,6 +517,12 @@
                 : null;
     }
 
+    Rect getFixedRotationBarContentFrame(int windowType) {
+        return isFixedRotationTransforming()
+                ? mFixedRotationTransformState.mBarContentFrames.get(windowType)
+                : null;
+    }
+
     InsetsState getFixedRotationTransformInsetsState() {
         return isFixedRotationTransforming() ? mFixedRotationTransformState.mInsetsState : null;
     }
@@ -526,12 +533,12 @@
         if (mFixedRotationTransformState != null) {
             return;
         }
-        final InsetsState insetsState = new InsetsState();
-        mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState,
-                mDisplayContent.getConfiguration().uiMode);
         mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
-                insetsState, new Configuration(config), mDisplayContent.getRotation());
+                new Configuration(config), mDisplayContent.getRotation());
         mFixedRotationTransformState.mAssociatedTokens.add(this);
+        mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
+                mFixedRotationTransformState.mInsetsState,
+                mFixedRotationTransformState.mBarContentFrames);
         onConfigurationChanged(getParent().getConfiguration());
         notifyFixedRotationTransform(true /* enabled */);
     }
@@ -554,6 +561,25 @@
         notifyFixedRotationTransform(true /* enabled */);
     }
 
+    /**
+     * Return {@code true} if one of the associated activity is still animating. Otherwise,
+     * return {@code false}.
+     */
+    boolean hasAnimatingFixedRotationTransition() {
+        if (mFixedRotationTransformState == null) {
+            return false;
+        }
+
+        for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) {
+            final ActivityRecord r =
+                    mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
+            if (r != null && r.isAnimating(TRANSITION | PARENTS)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void finishFixedRotationTransform() {
         finishFixedRotationTransform(null /* applyDisplayRotation */);
     }
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index e790a19..7132706 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -165,6 +165,11 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::disableReadLogs(int32_t storageId) {
+    mImpl.disableReadLogs(storageId);
+    return ok();
+}
+
 binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path,
                                                        int32_t* _aidl_return) {
     *_aidl_return = mImpl.makeDir(storageId, path);
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 68549f5..1015494 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -74,7 +74,7 @@
                                    std::vector<uint8_t>* _aidl_return) final;
     binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
     binder::Status deleteStorage(int32_t storageId) final;
-
+    binder::Status disableReadLogs(int32_t storageId) final;
     binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
                                            const std::string& libDirRelativePath,
                                            const std::string& abi, bool extractNativeLibs,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 885f4d2..3450c3a 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -60,6 +60,7 @@
     static constexpr auto storagePrefix = "st"sv;
     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
     static constexpr auto infoMdName = ".info"sv;
+    static constexpr auto readLogsDisabledMarkerName = ".readlogs_disabled"sv;
     static constexpr auto libDir = "lib"sv;
     static constexpr auto libSuffix = ".so"sv;
     static constexpr auto blockSize = 4096;
@@ -172,6 +173,13 @@
 
     return name;
 }
+
+static bool checkReadLogsDisabledMarker(std::string_view root) {
+    const auto markerPath = path::c_str(path::join(root, constants().readLogsDisabledMarkerName));
+    struct stat st;
+    return (::stat(markerPath, &st) == 0);
+}
+
 } // namespace
 
 IncrementalService::IncFsMount::~IncFsMount() {
@@ -618,6 +626,32 @@
     return it->second->second.storage;
 }
 
+void IncrementalService::disableReadLogs(StorageId storageId) {
+    std::unique_lock l(mLock);
+    const auto ifs = getIfsLocked(storageId);
+    if (!ifs) {
+        LOG(ERROR) << "disableReadLogs failed, invalid storageId: " << storageId;
+        return;
+    }
+    if (!ifs->readLogsEnabled()) {
+        return;
+    }
+    ifs->disableReadLogs();
+    l.unlock();
+
+    const auto metadata = constants().readLogsDisabledMarkerName;
+    if (auto err = mIncFs->makeFile(ifs->control,
+                                    path::join(ifs->root, constants().mount,
+                                               constants().readLogsDisabledMarkerName),
+                                    0777, idFromMetadata(metadata), {})) {
+        //{.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
+        LOG(ERROR) << "Failed to make marker file for storageId: " << storageId;
+        return;
+    }
+
+    setStorageParams(storageId, /*enableReadLogs=*/false);
+}
+
 int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
     const auto ifs = getIfs(storageId);
     if (!ifs) {
@@ -627,6 +661,11 @@
 
     const auto& params = ifs->dataLoaderStub->params();
     if (enableReadLogs) {
+        if (!ifs->readLogsEnabled()) {
+            LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
+            return -EPERM;
+        }
+
         if (auto status = mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage,
                                                           params.packageName.c_str());
             !status.isOk()) {
@@ -1072,6 +1111,11 @@
                                                 std::move(control), *this);
         cleanupFiles.release(); // ifs will take care of that now
 
+        // Check if marker file present.
+        if (checkReadLogsDisabledMarker(root)) {
+            ifs->disableReadLogs();
+        }
+
         std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints;
         auto d = openDir(root);
         while (auto e = ::readdir(d.get())) {
@@ -1243,6 +1287,11 @@
     ifs->mountId = mount.storage().id();
     mNextId = std::max(mNextId, ifs->mountId + 1);
 
+    // Check if marker file present.
+    if (checkReadLogsDisabledMarker(mountTarget)) {
+        ifs->disableReadLogs();
+    }
+
     // DataLoader params
     DataLoaderParamsParcel dataLoaderParams;
     {
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 918531b..a6cc946 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -94,6 +94,10 @@
         Permanent = 1,
     };
 
+    enum StorageFlags {
+        ReadLogsEnabled = 1,
+    };
+
     static FileId idFromMetadata(std::span<const uint8_t> metadata);
     static inline FileId idFromMetadata(std::span<const char> metadata) {
         return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
@@ -116,6 +120,7 @@
     int unbind(StorageId storage, std::string_view target);
     void deleteStorage(StorageId storage);
 
+    void disableReadLogs(StorageId storage);
     int setStorageParams(StorageId storage, bool enableReadLogs);
 
     int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -264,6 +269,7 @@
         const std::string root;
         Control control;
         /*const*/ MountId mountId;
+        int32_t flags = StorageFlags::ReadLogsEnabled;
         StorageMap storages;
         BindMap bindPoints;
         DataLoaderStubPtr dataLoaderStub;
@@ -282,6 +288,9 @@
 
         StorageMap::iterator makeStorage(StorageId id);
 
+        void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; }
+        int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
+
         static void cleanupFilesystem(std::string_view root);
     };
 
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 26b5094..1ae9e25 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -929,6 +929,34 @@
     ASSERT_GE(mDataLoader->setStorageParams(true), 0);
 }
 
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
+    mVold->mountIncFsSuccess();
+    mIncFs->makeFileSuccess();
+    mVold->bindMountSuccess();
+    mVold->setIncFsMountOptionsSuccess();
+    mDataLoaderManager->bindToDataLoaderSuccess();
+    mDataLoaderManager->getDataLoaderSuccess();
+    mAppOpsManager->checkPermissionSuccess();
+    EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+    EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    // Enabling and then disabling readlogs.
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    // After setIncFsMountOptions succeeded expecting to start watching.
+    EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+    // Not expecting callback removal.
+    EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    ASSERT_GE(storageId, 0);
+    ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+    // Now disable.
+    mIncrementalService->disableReadLogs(storageId);
+    ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
     mVold->mountIncFsSuccess();
     mIncFs->makeFileSuccess();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3b38d94..6db3233 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -80,6 +80,7 @@
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
     <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+    <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 3062584..b100c84 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -31,9 +31,10 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.RemoteException;
-import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+
+import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 import com.android.server.wm.WindowManagerInternal;
@@ -55,7 +56,6 @@
 import static junit.framework.TestCase.assertTrue;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -65,6 +65,7 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -100,6 +101,7 @@
     private BroadcastReceiver mTimeChangedCallback;
     private AlarmManager.OnAlarmListener mCustomListener;
     private Consumer<PowerSaveState> mPowerSaveConsumer;
+    private TwilightListener mTwilightListener;
 
     @Before
     public void setUp() {
@@ -107,6 +109,10 @@
         when(mContext.checkCallingOrSelfPermission(anyString()))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
         doAnswer(inv -> {
+            mTwilightListener = (TwilightListener) inv.getArgument(0);
+            return null;
+        }).when(mTwilightManager).registerListener(any(), any());
+        doAnswer(inv -> {
             mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1);
             return null;
         }).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any());
@@ -160,6 +166,37 @@
     }
 
     @Test
+    public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException {
+        // set up
+        when(mPowerManager.isInteractive()).thenReturn(false);
+        mService.setNightMode(MODE_NIGHT_NO);
+        assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+        // assume it is day time
+        doReturn(false).when(mTwilightState).isNight();
+
+        // set mode to auto
+        mService.setNightMode(MODE_NIGHT_AUTO);
+
+        // set night mode on overriding current config
+        mService.setNightModeActivated(true);
+
+        assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
+
+        // now it is night time
+        doReturn(true).when(mTwilightState).isNight();
+        mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+        assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
+
+        // now it is next day mid day
+        doReturn(false).when(mTwilightState).isNight();
+        mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+        assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+    }
+
+    @Test
     public void setAutoMode_screenOffRegistered() throws RemoteException {
         try {
             mService.setNightMode(MODE_NIGHT_NO);
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 4ee933a..29b96eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -794,6 +794,39 @@
     }
 
     /**
+     * Verify that when top focused activity is on secondary display, when finishing the top focused
+     * activity on default display, the preferred top stack on default display should be changed by
+     * adjusting focus.
+     */
+    @Test
+    public void testFinishActivityIfPossible_PreferredTopStackChanged() {
+        final ActivityRecord topActivityOnNonTopDisplay =
+                createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+        ActivityStack topRootableTask = topActivityOnNonTopDisplay.getRootTask();
+        topRootableTask.moveToFront("test");
+        assertTrue(topRootableTask.isTopStackInDisplayArea());
+        assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea()
+                .mPreferredTopFocusableStack);
+
+        final ActivityRecord secondaryDisplayActivity =
+                createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+        topRootableTask = secondaryDisplayActivity.getRootTask();
+        topRootableTask.moveToFront("test");
+        assertTrue(topRootableTask.isTopStackInDisplayArea());
+        assertEquals(topRootableTask,
+                secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableStack);
+
+        // The global top focus activity is on secondary display now.
+        // Finish top activity on default display and verify the next preferred top focusable stack
+        // on default display has changed.
+        topActivityOnNonTopDisplay.setState(RESUMED, "test");
+        topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */,
+                null /* resultGrants */, "test", false /* oomAdj */);
+        assertEquals(mTask, mStack.getTopMostTask());
+        assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack);
+    }
+
+    /**
      * Verify that resumed activity is paused due to finish request.
      */
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index ef28a45..a16bd2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -38,6 +38,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -452,6 +453,32 @@
         assertFalse(middle.isVisible());
     }
 
+    @Test
+    public void testTransferStartingWindowSetFixedRotation() {
+        mWm.mIsFixedRotationTransformEnabled = true;
+        final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask);
+        mTask.positionChildAt(topActivity, POSITION_TOP);
+        mActivity.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false);
+        waitUntilHandlersIdle();
+
+        // Make activities to have different rotation from it display and set fixed rotation
+        // transform to activity1.
+        int rotation = (mDisplayContent.getRotation() + 1) % 4;
+        mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation);
+        doReturn(rotation).when(mDisplayContent)
+                .rotationForActivityInDifferentOrientation(topActivity);
+
+        // Make sure the fixed rotation transform linked to activity2 when adding starting window
+        // on activity2.
+        topActivity.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(),
+                false, false, false, true, false);
+        waitUntilHandlersIdle();
+        assertTrue(topActivity.hasFixedRotationTransform());
+    }
+
     private ActivityRecord createIsolatedTestActivityRecord() {
         final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(taskStack, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4ad7dff..d3f677c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1144,7 +1144,14 @@
         assertTrue(app.hasFixedRotationTransform(app2));
         assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
 
+        // The fixed rotation transform can only be finished when all animation finished.
+        doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
+        mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
+        assertTrue(app.hasFixedRotationTransform());
+        assertTrue(app2.hasFixedRotationTransform());
+
         // The display should be rotated after the launch is finished.
+        doReturn(false).when(app).isAnimating(anyInt(), anyInt());
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
 
         // The fixed rotation should be cleared and the new rotation is applied to display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 27c4e9b..1922351 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -57,6 +57,7 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.InsetsState;
@@ -776,15 +777,15 @@
     }
 
     private void assertSimulateLayoutSameDisplayFrames() {
-        final int uiMode = 0;
         final String prefix = "";
         final InsetsState simulatedInsetsState = new InsetsState();
         final DisplayFrames simulatedDisplayFrames = createDisplayFrames();
-        mDisplayPolicy.beginLayoutLw(mFrames, uiMode);
+        mDisplayPolicy.beginLayoutLw(mFrames, mDisplayContent.getConfiguration().uiMode);
         // Force the display bounds because it is not synced with display frames in policy test.
         mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted);
         mDisplayContent.getInsetsStateController().onPostLayout();
-        mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, simulatedInsetsState, uiMode);
+        mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, simulatedInsetsState,
+                new SparseArray<>() /* barContentFrames */);
 
         final StringWriter realFramesDump = new StringWriter();
         mFrames.dump(prefix, new PrintWriter(realFramesDump));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index ca6679d..243468a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -349,11 +349,19 @@
         assertEquals(Configuration.ORIENTATION_PORTRAIT,
                 homeActivity.getConfiguration().orientation);
 
-        // Home activity won't become top (return to landActivity), so its fixed rotation and the
-        // top rotated record should be cleared.
+        // Home activity won't become top (return to landActivity), so the top rotated record should
+        // be cleared.
         mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        assertFalse(homeActivity.hasFixedRotationTransform());
+        assertFalse(mDefaultDisplay.isFixedRotationLaunchingApp(homeActivity));
         assertFalse(mDefaultDisplay.hasTopFixedRotationLaunchingApp());
+        // The transform should keep until the transition is done, so the restored configuration
+        // won't be sent to activity and cause unnecessary configuration change.
+        assertTrue(homeActivity.hasFixedRotationTransform());
+
+        // In real case the transition will be executed from RecentsAnimation#finishAnimation.
+        mDefaultDisplay.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(
+                homeActivity.token);
+        assertFalse(homeActivity.hasFixedRotationTransform());
     }
 
     @Test
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 665cf83..15b395c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -24,6 +24,7 @@
 import static android.view.Surface.ROTATION_90;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -288,14 +289,29 @@
 
         // Move the non-resizable activity to the new display.
         mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
-        // The configuration bounds should keep the same.
+        // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
         assertEquals(origWidth, configBounds.width());
         assertEquals(origHeight, configBounds.height());
         assertScaled();
 
+        final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
         // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
-        assertEquals(newDisplay.getBounds().height() - notchHeight,
+        assertEquals(newDisplayBounds.height() - notchHeight,
                 (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+
+        // Recompute the natural configuration in the new display.
+        mActivity.clearSizeCompatMode();
+        mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+        // Because the display cannot rotate, the portrait activity will fit the short side of
+        // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
+        assertEquals(newDisplayBounds.height(), configBounds.height());
+        assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+                configBounds.width());
+        assertFitted();
+        // The appBounds should be [200, 100 - 700, 1000].
+        final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
+        assertEquals(configBounds.width(), appBounds.width());
+        assertEquals(configBounds.height() - notchHeight, appBounds.height());
     }
 
     @Test
@@ -491,7 +507,10 @@
         mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
         final int dw = 1000;
         final int dh = 2500;
-        setUpDisplaySizeWithApp(dw, dh);
+        final int notchHeight = 200;
+        setUpApp(new TestDisplayContent.Builder(mService, dw, dh).setNotch(notchHeight).build());
+        addStatusBar(mActivity.mDisplayContent);
+
         mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
                 false /* alwaysKeepCurrent */);
         mActivity.mDisplayContent.mOpeningApps.add(mActivity);
@@ -503,31 +522,76 @@
         // Display keeps in original orientation.
         assertEquals(Configuration.ORIENTATION_PORTRAIT,
                 mActivity.mDisplayContent.getConfiguration().orientation);
-        // Activity bounds should be [350, 0 - 2150, 1000] in landscape. Its width=1000*1.8=1800.
+        // The width should be restricted by the max aspect ratio = 1000 * 1.8 = 1800.
         assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
-        // The bounds should be horizontal centered: (2500-1900)/2=350.
-        assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);
+        // The notch is at the left side of the landscape activity. The bounds should be horizontal
+        // centered in the remaining area [200, 0 - 2500, 1000], so its left should be
+        // 200 + (2300 - 1800) / 2 = 450. The bounds should be [450, 0 - 2250, 1000].
+        assertEquals(notchHeight + (dh - notchHeight - mActivity.getBounds().width()) / 2,
+                mActivity.getBounds().left);
 
         // The letterbox needs a main window to layout.
-        addWindowToActivity(mActivity);
+        final WindowState w = addWindowToActivity(mActivity);
         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
         mActivity.mRootWindowContainer.performSurfacePlacement();
-        // The letterbox insets should be [350, 0 - 350, 0].
+        // The letterbox insets should be [450, 0 - 250, 0].
         assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
                 mActivity.getLetterboxInsets());
+
+        final StatusBarController statusBarController =
+                mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
+        // The activity doesn't fill the display, so the letterbox of the rotated activity is
+        // overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
+        // be transparent.
+        assertFalse(statusBarController.isTransparentAllowed(w));
+
+        // Make the activity fill the display.
+        prepareUnresizable(10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
+        w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+        // Refresh the letterbox.
+        mActivity.mRootWindowContainer.performSurfacePlacement();
+
+        // The letterbox should only cover the notch area, so status bar can be transparent.
+        assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
+        assertTrue(statusBarController.isTransparentAllowed(w));
     }
 
-    private WindowState addWindowToActivity(ActivityRecord activity) {
+    private static WindowState addWindowToActivity(ActivityRecord activity) {
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
         params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
         final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
-                mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+                activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
         WindowTestsBase.makeWindowVisible(w);
         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
-        mActivity.addWindow(w);
+        activity.addWindow(w);
         return w;
     }
 
+    private static void addStatusBar(DisplayContent displayContent) {
+        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+        doReturn(true).when(displayPolicy).hasStatusBar();
+        displayPolicy.onConfigurationChanged();
+
+        final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
+                WindowManager.LayoutParams.TYPE_STATUS_BAR, displayContent);
+        final WindowManager.LayoutParams attrs =
+                new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_STATUS_BAR);
+        attrs.gravity = android.view.Gravity.TOP;
+        attrs.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        attrs.setFitInsetsTypes(0 /* types */);
+        final WindowTestUtils.TestWindowState statusBar = new WindowTestUtils.TestWindowState(
+                displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
+        token.addWindow(statusBar);
+        statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
+                displayContent.getDisplayUiContext().getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.status_bar_height));
+
+        displayPolicy.addWindowLw(statusBar, attrs);
+        displayPolicy.beginLayoutLw(displayContent.mDisplayFrames,
+                displayContent.getConfiguration().uiMode);
+    }
+
     /**
      * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
      * orientation.