Merge "Updating rotation transaction behaviour" into rvc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 687b1d4..f360843 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -463,9 +463,6 @@
                 userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
             }
 
-            // Offload to handler thread to avoid boottime impact.
-            mHandler.post(this::loadHeadlessSystemAppCache);
-
             if (mPendingInitializeDefaults || !userFileExists) {
                 initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
             }
@@ -475,6 +472,10 @@
             }
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setChargingState(mInjector.isCharging());
+
+            // Offload to handler thread after boot completed to avoid boot time impact. This means
+            // that headless system apps may be put in a lower bucket until boot has completed.
+            mHandler.post(this::loadHeadlessSystemAppCache);
         }
     }
 
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 5722f92..6d9beb8 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -60,11 +60,11 @@
 }
 
 int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
-    if (currentTimeSec <= mAlarmSec) {
+    if (currentTimeSec < mAlarmSec) {
         return mAlarmSec;
     }
     int64_t periodsForward =
-        ((currentTimeSec - mAlarmSec) * MS_PER_SEC - 1) / mAlarmConfig.period_millis() + 1;
+        ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1;
     return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
 }
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 02c0763..12e428c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -4948,6 +4948,8 @@
         ERROR_DURING_COMMIT = 2;
         // Commit Failed: Digest of the data did not match Blob digest
         DIGEST_MISMATCH = 3;
+        // Commit Failed: Allowed count limit exceeded
+        COUNT_LIMIT_EXCEEDED = 4;
     }
     optional Result result = 4;
 }
@@ -4980,6 +4982,8 @@
         LEASE_EXPIRY_INVALID = 4;
         // Lease Failed: Leasee has exceeded the total data lease limit
         DATA_SIZE_LIMIT_EXCEEDED = 5;
+        // Leasee Failed: Allowed count limit exceeded
+        COUNT_LIMIT_EXCEEDED = 6;
     }
     optional Result result = 4;
 }
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 322cfaf..64ea219 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -43,23 +43,47 @@
     alarm.set_offset_millis(15 * MS_PER_SEC);
     alarm.set_period_millis(60 * 60 * MS_PER_SEC);  // 1hr
     int64_t startMillis = 100000000 * MS_PER_SEC;
+    int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15;
     AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor);
 
-    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+    EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
 
     uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
     std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
         subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
     EXPECT_TRUE(firedAlarmSet.empty());
     tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
-    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15));
+    EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+    EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
 
     currentTimeSec = startMillis / MS_PER_SEC + 7000;
+    nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
     firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
     ASSERT_EQ(firedAlarmSet.size(), 1u);
     tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
     EXPECT_TRUE(firedAlarmSet.empty());
-    EXPECT_EQ(tracker.mAlarmSec, (int64_t)(startMillis / MS_PER_SEC + 15 + 2 * 60 * 60));
+    EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+    EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+    // Alarm fires exactly on time.
+    currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
+    nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60;
+    firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    ASSERT_EQ(firedAlarmSet.size(), 1u);
+    tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+    EXPECT_TRUE(firedAlarmSet.empty());
+    EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+    EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
+
+    // Alarm fires exactly 1 period late.
+    currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60;
+    nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60;
+    firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
+    ASSERT_EQ(firedAlarmSet.size(), 1u);
+    tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
+    EXPECT_TRUE(firedAlarmSet.empty());
+    EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
+    EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
 }
 
 }  // namespace statsd
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 2d2dda0..e385cd2 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -121,7 +121,7 @@
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
-         * requirements for <strong>Tier 3</strong> (formerly <strong>Strong</strong>), as defined
+         * requirements for <strong>Class 3</strong> (formerly <strong>Strong</strong>), as defined
          * by the Android CDD.
          *
          * <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
@@ -132,7 +132,7 @@
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
-         * requirements for <strong>Tier 2</strong> (formerly <strong>Weak</strong>), as defined by
+         * requirements for <strong>Class 2</strong> (formerly <strong>Weak</strong>), as defined by
          * the Android CDD.
          *
          * <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
@@ -142,7 +142,7 @@
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
-         * requirements for <strong>Tier 1</strong> (formerly <strong>Convenience</strong>), as
+         * requirements for <strong>Class 1</strong> (formerly <strong>Convenience</strong>), as
          * defined by the Android CDD.
          *
          * <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a30c3c5..8c2358d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -819,7 +819,8 @@
             if (chooserListAdapter.getCount() == 0) {
                 return;
             }
-            if (resultList.isEmpty()) {
+            if (resultList.isEmpty()
+                    && shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
                 // APS may be disabled, so try querying targets ourselves.
                 queryDirectShareTargets(chooserListAdapter, true);
                 return;
@@ -2025,6 +2026,26 @@
         });
     }
 
+    /**
+     * Returns {@code false} if {@code userHandle} is the work profile and it's either
+     * in quiet mode or not running.
+     */
+    private boolean shouldQueryShortcutManager(UserHandle userHandle) {
+        if (!shouldShowTabs()) {
+            return true;
+        }
+        if (!getWorkProfileUserHandle().equals(userHandle)) {
+            return true;
+        }
+        if (!isUserRunning(userHandle)) {
+            return false;
+        }
+        if (isQuietModeEnabled(userHandle)) {
+            return false;
+        }
+        return true;
+    }
+
     private void sendChooserTargetRankingScore(List<AppTarget> chooserTargetScores,
             UserHandle userHandle) {
         final Message msg = Message.obtain();
@@ -2839,8 +2860,8 @@
             return;
         }
 
-        // no need to query direct share for work profile when its turned off
-        if (isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
+        // no need to query direct share for work profile when its locked or disabled
+        if (!shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
             getChooserActivityLogger().logSharesheetAppLoadComplete();
             return;
         }
@@ -2865,6 +2886,12 @@
     }
 
     @VisibleForTesting
+    protected boolean isUserRunning(UserHandle userHandle) {
+        UserManager userManager = getSystemService(UserManager.class);
+        return userManager.isUserRunning(userHandle);
+    }
+
+    @VisibleForTesting
     protected boolean isQuietModeEnabled(UserHandle userHandle) {
         UserManager userManager = getSystemService(UserManager.class);
         return userManager.isQuietModeEnabled(userHandle);
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 31e6cb9..00b5cb6 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -917,6 +917,7 @@
         if (getAppPredictor() != null) {
             getAppPredictor().unregisterPredictionUpdates(mAppPredictorCallback);
             getAppPredictor().destroy();
+            setAppPredictor(null);
         }
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 49de7c8..0bf10cb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1995,6 +1995,70 @@
                 isQueryTargetServicesCalledOnWorkProfile[0]);
     }
 
+    @Test
+    public void testWorkTab_selectingWorkTabWithLockedWorkUser_directShareTargetsNotQueried() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        sOverrides.isWorkProfileUserRunning = false;
+        boolean[] isQueryDirectShareCalledOnWorkProfile = new boolean[] { false };
+        sOverrides.onQueryDirectShareTargets = chooserListAdapter -> {
+            isQueryDirectShareCalledOnWorkProfile[0] =
+                    (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+            return null;
+        };
+        boolean[] isQueryTargetServicesCalledOnWorkProfile = new boolean[] { false };
+        sOverrides.onQueryTargetServices = chooserListAdapter -> {
+            isQueryTargetServicesCalledOnWorkProfile[0] =
+                    (chooserListAdapter.getUserHandle().getIdentifier() == 10);
+            return null;
+        };
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        assertFalse("Direct share targets were queried on a locked work profile user",
+                isQueryDirectShareCalledOnWorkProfile[0]);
+        assertFalse("Target services were queried on a locked work profile user",
+                isQueryTargetServicesCalledOnWorkProfile[0]);
+    }
+
+    @Test
+    public void testWorkTab_workUserLocked_workTargetsShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+        sOverrides.isWorkProfileUserRunning = false;
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        assertEquals(3, activity.getWorkListAdapter().getCount());
+    }
+
     private Intent createChooserIntent(Intent intent, Intent[] initialIntents) {
         Intent chooserIntent = new Intent();
         chooserIntent.setAction(Intent.ACTION_CHOOSER);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 0f6b51f..b7d6c61 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -227,6 +227,11 @@
         return sOverrides.isQuietModeEnabled;
     }
 
+    @Override
+    protected boolean isUserRunning(UserHandle userHandle) {
+        return sOverrides.isWorkProfileUserRunning;
+    }
+
     /**
      * We cannot directly mock the activity created since instrumentation creates it.
      * <p>
@@ -252,6 +257,7 @@
         public UserHandle workProfileUserHandle;
         public boolean hasCrossProfileIntents;
         public boolean isQuietModeEnabled;
+        public boolean isWorkProfileUserRunning;
         public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
         public PackageManager packageManager;
 
@@ -274,6 +280,7 @@
             workProfileUserHandle = null;
             hasCrossProfileIntents = true;
             isQuietModeEnabled = false;
+            isWorkProfileUserRunning = true;
             packageManager = null;
             multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
                 @Override
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 6792c64..8441282 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -27,6 +27,14 @@
     android:forceHasOverlappingRendering="false"
     android:background="@drawable/qs_media_background">
 
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/center_vertical_guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_percent="0.5"
+        />
+
     <!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
     <FrameLayout
         android:id="@+id/notification_media_progress_time"
@@ -133,6 +141,7 @@
             android:layout_width="@dimen/qs_seamless_icon_size"
             android:layout_height="@dimen/qs_seamless_icon_size"
             android:layout_marginEnd="8dp"
+            android:layout_gravity="center_vertical"
             android:tint="@color/media_primary_text"
             android:src="@*android:drawable/ic_media_seamless" />
 
@@ -140,6 +149,7 @@
             android:id="@+id/media_seamless_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
             android:fontFamily="@*android:string/config_headlineFontFamily"
             android:singleLine="true"
             android:text="@*android:string/ext_media_seamless_action"
@@ -156,15 +166,6 @@
         android:src="@drawable/ic_cast_connected"
         android:forceHasOverlappingRendering="false" />
 
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/media_seamless_barrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierDirection="start"
-        app:constraint_referenced_ids="media_seamless,media_seamless_fallback"
-        app:barrierAllowsGoneWidgets="false"
-        />
-
     <!-- Seek Bar -->
     <!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
     <SeekBar
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f1a0e3e..18f79df 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1269,6 +1269,7 @@
     <dimen name="qs_media_padding">16dp</dimen>
     <dimen name="qs_media_panel_outer_padding">16dp</dimen>
     <dimen name="qs_media_album_size">52dp</dimen>
+    <dimen name="qs_center_guideline_padding">10dp</dimen>
     <dimen name="qs_seamless_icon_size">20dp</dimen>
     <dimen name="qs_seamless_fallback_icon_size">20dp</dimen>
     <dimen name="qs_seamless_fallback_top_margin">18dp</dimen>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index 811e0e3..0926a98 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -31,12 +31,12 @@
         android:id="@+id/app_name"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="10dp"
+        android:layout_marginEnd="@dimen/qs_center_guideline_padding"
         android:layout_marginStart="10dp"
         android:layout_marginTop="20dp"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@id/icon"
-        app:layout_constraintEnd_toStartOf="@id/media_seamless_barrier"
+        app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
         app:layout_constraintHorizontal_bias="0"
         />
 
@@ -47,8 +47,11 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintWidth_min="60dp"
+        app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+        app:layout_constraintHorizontal_bias="1"
         android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
         android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_center_guideline_padding"
         />
 
     <Constraint
@@ -57,10 +60,13 @@
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
         android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
         android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+        android:layout_marginStart="@dimen/qs_center_guideline_padding"
         android:alpha="0.5"
         android:visibility="gone"
+        app:layout_constraintHorizontal_bias="1"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
         />
 
     <Constraint
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index 8432abc..dd15d5d 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -31,12 +31,12 @@
         android:id="@+id/app_name"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="10dp"
+        android:layout_marginEnd="@dimen/qs_center_guideline_padding"
         android:layout_marginStart="10dp"
         android:layout_marginTop="20dp"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toEndOf="@id/icon"
-        app:layout_constraintEnd_toStartOf="@id/media_seamless_barrier"
+        app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
         app:layout_constraintHorizontal_bias="0"
         />
 
@@ -46,9 +46,12 @@
         android:layout_height="wrap_content"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+        app:layout_constraintHorizontal_bias="1"
         app:layout_constraintWidth_min="60dp"
         android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
         android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+        android:layout_marginStart="@dimen/qs_center_guideline_padding"
         />
 
     <Constraint
@@ -57,10 +60,13 @@
         android:layout_height="@dimen/qs_seamless_fallback_icon_size"
         android:layout_marginTop="@dimen/qs_seamless_fallback_top_margin"
         android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+        android:layout_marginStart="@dimen/qs_center_guideline_padding"
         android:alpha="0.5"
         android:visibility="gone"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+        app:layout_constraintHorizontal_bias="1"
         />
 
     <Constraint
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ee31706..7792afa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -251,7 +251,8 @@
     private boolean mDeviceProvisioned;
 
     // Battery status
-    private BatteryStatus mBatteryStatus;
+    @VisibleForTesting
+    BatteryStatus mBatteryStatus;
 
     private StrongAuthTracker mStrongAuthTracker;
 
@@ -1698,6 +1699,17 @@
                     .getServiceStateForSubscriber(subId);
             mHandler.sendMessage(
                     mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
+
+            // Get initial state. Relying on Sticky behavior until API for getting info.
+            if (mBatteryStatus == null) {
+                Intent intent = mContext.registerReceiver(
+                        null,
+                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+                );
+                if (intent != null && mBatteryStatus == null) {
+                    mBroadcastReceiver.onReceive(mContext, intent);
+                }
+            }
         });
 
         mHandler.post(this::registerRingerTracker);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 2bfe015..1211fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -495,12 +495,9 @@
         }
         final float alpha = visibility ? 1f : 0f;
 
-        if (alpha == mActivityView.getAlpha()) {
-            return;
-        }
-
         mPointerView.setAlpha(alpha);
-        if (mActivityView != null) {
+
+        if (mActivityView != null && alpha != mActivityView.getAlpha()) {
             mActivityView.setAlpha(alpha);
             mActivityView.bringToFront();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index c073642..22d6b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -128,15 +128,22 @@
         }
 
     private fun bouncerOrRun(action: Action) {
-        if (!keyguardStateController.isUnlocked()) {
-            context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+        if (keyguardStateController.isShowing()) {
+            var closeGlobalActions = !keyguardStateController.isUnlocked()
+            if (closeGlobalActions) {
+                context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
 
-            // pending actions will only run after the control state has been refreshed
-            pendingAction = action
+                // pending actions will only run after the control state has been refreshed
+                pendingAction = action
+            }
 
             activityStarter.dismissKeyguardThenExecute({
                 Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
-                globalActionsComponent.handleShowGlobalActionsMenu()
+                if (closeGlobalActions) {
+                    globalActionsComponent.handleShowGlobalActionsMenu()
+                } else {
+                    action.invoke()
+                }
                 true
             }, { pendingAction = null }, true /* afterKeyguardGone */)
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index eed5200..b2e9164 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -2637,10 +2637,10 @@
     }
 
     private boolean shouldShowControls() {
-        return (mKeyguardStateController.isUnlocked() || mShowLockScreenCardsAndControls)
-                && controlsAvailable()
-                && mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
-                    != STRONG_AUTH_REQUIRED_AFTER_BOOT;
+        boolean showOnLockScreen = mShowLockScreenCardsAndControls && mLockPatternUtils
+                .getStrongAuthForUser(getCurrentUser().id) != STRONG_AUTH_REQUIRED_AFTER_BOOT;
+        return controlsAvailable()
+                && (mKeyguardStateController.isUnlocked() || showOnLockScreen);
     }
 
     private boolean controlsAvailable() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5052386..d6b6660 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -193,12 +193,15 @@
     private fun clearData() {
         // Called on user change. Remove all current MediaData objects and inform listeners
         val listenersCopy = listeners.toSet()
-        mediaEntries.forEach {
+        val keyCopy = mediaEntries.keys.toMutableList()
+        // Clear the list first, to make sure callbacks from listeners if we have any entries
+        // are up to date
+        mediaEntries.clear()
+        keyCopy.forEach {
             listenersCopy.forEach { listener ->
-                listener.onMediaDataRemoved(it.key)
+                listener.onMediaDataRemoved(it)
             }
         }
-        mediaEntries.clear()
     }
 
     private fun removeAllForPackage(packageName: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 078c540..66804be 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -225,6 +225,8 @@
     @VisibleForTesting
     final class Receiver extends BroadcastReceiver {
 
+        private boolean mHasReceivedBattery = false;
+
         public void init() {
             // Register for Intent broadcasts for...
             IntentFilter filter = new IntentFilter();
@@ -234,6 +236,17 @@
             filter.addAction(Intent.ACTION_SCREEN_ON);
             filter.addAction(Intent.ACTION_USER_SWITCHED);
             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
+            // Force get initial values. Relying on Sticky behavior until API for getting info.
+            if (!mHasReceivedBattery) {
+                // Get initial state
+                Intent intent = mContext.registerReceiver(
+                        null,
+                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+                );
+                if (intent != null) {
+                    onReceive(mContext, intent);
+                }
+            }
         }
 
         @Override
@@ -246,6 +259,7 @@
                     }
                 });
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                mHasReceivedBattery = true;
                 final int oldBatteryLevel = mBatteryLevel;
                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
                 final int oldBatteryStatus = mBatteryStatus;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b12e47..eba4465 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -96,6 +96,11 @@
                 mAnimateBottomOnNextLayout = true;
             }
         });
+        mQSPanel.setMediaVisibilityChangedListener((visible) -> {
+            if (mQSPanel.isShown()) {
+                mAnimateBottomOnNextLayout = true;
+            }
+        });
 
 
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 00419e6..7f35161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -74,7 +74,8 @@
     protected boolean mPowerSave;
     private boolean mAodPowerSave;
     private boolean mTestmode = false;
-    private boolean mHasReceivedBattery = false;
+    @VisibleForTesting
+    boolean mHasReceivedBattery = false;
     private Estimate mEstimate;
     private boolean mFetchingEstimate = false;
 
@@ -102,6 +103,16 @@
     @Override
     public void init() {
         registerReceiver();
+        if (!mHasReceivedBattery) {
+            // Get initial state. Relying on Sticky behavior until API for getting info.
+            Intent intent = mContext.registerReceiver(
+                    null,
+                    new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+            );
+            if (intent != null && !mHasReceivedBattery) {
+                onReceive(mContext, intent);
+            }
+        }
         updatePowerSave();
         updateEstimate();
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7bc453a..9e056cf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -211,6 +211,13 @@
     }
 
     @Test
+    public void testInitialBatteryLevelRequested() {
+        mTestableLooper.processAllMessages();
+
+        assertThat(mKeyguardUpdateMonitor.mBatteryStatus).isNotNull();
+    }
+
+    @Test
     public void testReceiversRegistered() {
         verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
                 eq(mKeyguardUpdateMonitor.mBroadcastReceiver),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index 95ff98a..f29f042 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -67,27 +67,35 @@
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-        mRegisteredReceivers.add(receiver);
+        if (receiver != null) {
+            mRegisteredReceivers.add(receiver);
+        }
         return super.registerReceiver(receiver, filter);
     }
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
-        mRegisteredReceivers.add(receiver);
+        if (receiver != null) {
+            mRegisteredReceivers.add(receiver);
+        }
         return super.registerReceiver(receiver, filter, broadcastPermission, scheduler);
     }
 
     @Override
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
-        mRegisteredReceivers.add(receiver);
+        if (receiver != null) {
+            mRegisteredReceivers.add(receiver);
+        }
         return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, scheduler);
     }
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
-        mRegisteredReceivers.remove(receiver);
+        if (receiver != null) {
+            mRegisteredReceivers.remove(receiver);
+        }
         super.unregisterReceiver(receiver);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index f83fbd4..eca48c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -59,6 +59,11 @@
     }
 
     @Test
+    public void testBatteryInitialized() {
+        Assert.assertTrue(mBatteryController.mHasReceivedBattery);
+    }
+
+    @Test
     public void testIndependentAODBatterySaver_true() {
         PowerSaveState state = new PowerSaveState.Builder()
                 .setBatterySaverEnabled(true)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e77b361..c9dbacd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8086,6 +8086,12 @@
     }
 
     int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
+        if (Thread.holdsLock(mActivityTaskManager.getGlobalLock())) {
+            Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
+                    + " because caller is holding WM lock; assuming permission denied"));
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         final String name = uri.getAuthority();
         final long ident = Binder.clearCallingIdentity();
         ContentProviderHolder holder = null;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 31712ef..b378621 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -640,13 +640,10 @@
             }
 
             // If the caller hasn't already resolved the activity, we're willing
-            // to do so here, but because that may require acquiring the AM lock
-            // as part of calculating the NeededUriGrants, we must never hold
-            // the WM lock here to avoid deadlocking.
+            // to do so here. If the caller is already holding the WM lock here,
+            // and we need to check dynamic Uri permissions, then we're forced
+            // to assume those permissions are denied to avoid deadlocking.
             if (mRequest.activityInfo == null) {
-                if (Thread.holdsLock(mService.mGlobalLock)) {
-                    Slog.wtf(TAG, new IllegalStateException("Caller must not hold WM lock"));
-                }
                 mRequest.resolveActivity(mSupervisor);
             }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a7b1209..748244e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4515,6 +4515,7 @@
      */
     void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) {
         setMainWindowSizeChangeTransaction(t, this);
+        forAllWindows(WindowState::requestRedrawForSync, true);
     }
 
     private void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t, Task origin) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 708abe2..f1acee5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -339,7 +339,7 @@
     private boolean mDragResizing;
     private boolean mDragResizingChangeReported = true;
     private int mResizeMode;
-    private boolean mResizeForBlastSyncReported;
+    private boolean mRedrawForSyncReported;
 
     /**
      * Special mode that is intended only for the rounded corner overlay: during rotation
@@ -1402,7 +1402,7 @@
                 || configChanged
                 || dragResizingChanged
                 || mReportOrientationChanged
-                || requestResizeForBlastSync()) {
+                || shouldSendRedrawForSync()) {
             ProtoLog.v(WM_DEBUG_RESIZE,
                         "Resize reasons for w=%s:  %s surfaceResized=%b configChanged=%b "
                                 + "dragResizingChanged=%b reportOrientationChanged=%b",
@@ -3564,7 +3564,6 @@
         mReportOrientationChanged = false;
         mDragResizingChangeReported = true;
         mWinAnimator.mSurfaceResized = false;
-        mResizeForBlastSyncReported = true;
         mWindowFrames.resetInsetsChanged();
 
         final Rect frame = mWindowFrames.mCompatFrame;
@@ -3572,11 +3571,13 @@
         final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
         final Rect stableInsets = mWindowFrames.mLastStableInsets;
         final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
-        final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync();
-        final boolean forceRelayout = reportOrientation || isDragResizeChanged();
+        final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported;
+        final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported;
         final int displayId = getDisplayId();
         final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
 
+        mRedrawForSyncReported = true;
+
         try {
             mClient.resized(frame, contentInsets, visibleInsets, stableInsets, reportDraw,
                     mergedConfiguration, getBackdropFrame(frame), forceRelayout,
@@ -5823,7 +5824,7 @@
         if (!willSync) {
             return false;
         }
-        mResizeForBlastSyncReported = false;
+        requestRedrawForSync();
 
         mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
         addChildrenToSyncSet(mLocalSyncId);
@@ -5884,7 +5885,20 @@
         notifyBlastSyncTransaction();
     }
 
-    private boolean requestResizeForBlastSync() {
-        return useBLASTSync() && !mResizeForBlastSyncReported;
+    /**
+     * When using the two WindowOrganizer sync-primitives (BoundsChangeTransaction, BLASTSync)
+     * it can be a little difficult to predict whether your change will actually trigger redrawing
+     * on the client side. To ease the burden on shell developers, we force send MSG_RESIZED
+     * for Windows involved in these Syncs
+     */
+    private boolean shouldSendRedrawForSync() {
+        final Task task = getTask();
+        if (task != null && task.getMainWindowSizeChangeTransaction() != null)
+            return !mRedrawForSyncReported;
+        return useBLASTSync() && !mRedrawForSyncReported;
+    }
+
+    void requestRedrawForSync() {
+        mRedrawForSyncReported = false;
     }
 }
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 6b19455..67e1b44 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -120,7 +120,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.INTENT_STARTED));
     state = State.INTENT_STARTED;
   }
 
@@ -138,7 +138,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.INTENT_FAILED));
     state = State.INTENT_FAILED;
   }
 
@@ -156,7 +156,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
     state = State.ACTIVITY_LAUNCHED;
   }
 
@@ -174,7 +174,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED));
     state = State.ACTIVITY_CANCELLED;
   }
 
@@ -194,7 +194,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED));
     state = State.ACTIVITY_FINISHED;
   }
 
@@ -215,7 +215,7 @@
       return;
     }
 
-    Log.i(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
+    Log.d(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
     state = State.REPORT_FULLY_DRAWN;
   }
 
@@ -238,7 +238,7 @@
       state = State.UNKNOWN;
     }
     ++accIntentStartedEvents;
-    Log.i(TAG,
+    Log.d(TAG,
         String.format("inc AccIntentStartedEvents to %d", accIntentStartedEvents));
   }
 
@@ -250,7 +250,7 @@
       state = State.INIT;
     }
     --accIntentStartedEvents;
-    Log.i(TAG,
+    Log.d(TAG,
         String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
   }
 
@@ -258,6 +258,6 @@
     StringWriter sw = new StringWriter();
     PrintWriter pw = new PrintWriter(sw);
     new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
-    Log.w(TAG, String.format("%s\n%s", log, sw));
+    Log.d(TAG, String.format("%s\n%s", log, sw));
   }
 }