Merge "Request scroll capture when the screen rotates." into sc-dev
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index dc29c5e..a741f96 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -265,7 +265,7 @@
                 // Return an empty cursor for all columns.
                 return new MatrixCursor(cursor.getColumnNames(), 0);
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "query");
+            traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -285,7 +285,7 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "getType");
+            traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
             try {
                 return mInterface.getType(uri);
             } catch (RemoteException e) {
@@ -323,7 +323,7 @@
                     setCallingAttributionSource(original);
                 }
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "insert");
+            traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -345,7 +345,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "bulkInsert");
+            traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -391,7 +391,7 @@
                     }
                 }
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "applyBatch");
+            traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -423,7 +423,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "delete");
+            traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -445,7 +445,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "update");
+            traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -465,7 +465,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "openFile");
+            traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -486,7 +486,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "openAssetFile");
+            traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -505,7 +505,7 @@
                 String method, @Nullable String arg, @Nullable Bundle extras) {
             validateIncomingAuthority(authority);
             Bundle.setDefusable(extras, true);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "call");
+            traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -523,7 +523,7 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "getStreamTypes");
+            traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
             try {
                 return mInterface.getStreamTypes(uri, mimeTypeFilter);
             } catch (RemoteException e) {
@@ -541,7 +541,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, "r");
-            Trace.traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile");
+            traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -569,7 +569,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "canonicalize");
+            traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -605,7 +605,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "uncanonicalize");
+            traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -641,7 +641,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return false;
             }
-            Trace.traceBegin(TRACE_TAG_DATABASE, "refresh");
+            traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -658,7 +658,7 @@
                 int uid, int modeFlags) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission");
+            traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -2683,4 +2683,10 @@
         }
         return uri;
     }
+
+    private static void traceBegin(long traceTag, String methodName, String subInfo) {
+        if (Trace.isTagEnabled(traceTag)) {
+            Trace.traceBegin(traceTag, methodName + subInfo);
+        }
+    }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index f3cc35b3..c5d0cd4 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -158,6 +158,8 @@
         mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
                 "permissionmgr"));
         mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
+        //TODO ntmyren: there should be a way to only enable the watcher when requested
+        mUsageHelper = new PermissionUsageHelper(context);
     }
 
     /**
@@ -878,10 +880,6 @@
     @NonNull
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
     public List<PermGroupUsage> getIndicatorAppOpUsageData() {
-        // Lazily initialize the usage helper
-        if (mUsageHelper == null) {
-            mUsageHelper = new PermissionUsageHelper(mContext);
-        }
         return mUsageHelper.getOpUsageData(new AudioManager().isMicrophoneMute());
     }
 
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 53ba259..d4e548e 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -19,6 +19,10 @@
 import static android.Manifest.permission_group.CAMERA;
 import static android.Manifest.permission_group.LOCATION;
 import static android.Manifest.permission_group.MICROPHONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.AttributionFlags;
 import static android.app.AppOpsManager.OPSTR_CAMERA;
 import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION;
 import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
@@ -30,6 +34,7 @@
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -56,7 +61,7 @@
  *
  * @hide
  */
-public class PermissionUsageHelper {
+public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener {
 
     /** Whether to show the mic and camera icons.  */
     private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
@@ -140,6 +145,7 @@
     private ArrayMap<UserHandle, Context> mUserContexts;
     private PackageManager mPkgManager;
     private AppOpsManager mAppOpsManager;
+    private ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains = new ArrayMap<>();
 
     /**
      * Constructor for PermissionUsageHelper
@@ -151,6 +157,10 @@
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mUserContexts = new ArrayMap<>();
         mUserContexts.put(Process.myUserHandle(), mContext);
+        // TODO ntmyren: make this listen for flag enable/disable changes
+        String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+        mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops,
+                context.getMainExecutor(), this);
     }
 
     private Context getUserContext(UserHandle user) {
@@ -160,6 +170,45 @@
         return mUserContexts.get(user);
     }
 
+    @Override
+    public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
+            boolean active) {
+        // not part of an attribution chain. Do nothing
+    }
+
+    @Override
+    public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
+            @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags,
+            int attributionChainId) {
+        if ((attributionFlags & ATTRIBUTION_FLAGS_NONE) != 0) {
+            return;
+        }
+
+        if (!active) {
+            // if any link in the chain is finished, remove the chain.
+            // TODO ntmyren: be smarter about this
+            mAttributionChains.remove(attributionChainId);
+            return;
+        }
+
+        ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
+                attributionChainId, k -> new ArrayList<>());
+        AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid,
+                attributionFlags);
+
+        int currSize = currentChain.size();
+        if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) {
+            // if the list is empty, this link is the end, or the last link in the current chain
+            // isn't the end, add it to the end
+            currentChain.add(link);
+        } else if (link.isStart()) {
+            currentChain.add(0, link);
+        } else if (currentChain.get(currentChain.size() - 1).isEnd()) {
+            // we already have the end, and this is a mid node, so insert before the end
+            currentChain.add(currSize - 1, link);
+        }
+    }
+
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
@@ -331,7 +380,7 @@
     private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
         ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
 
-        if (usages == null) {
+        if (usages == null || usages.isEmpty()) {
             return usagesAndLabels;
         }
 
@@ -430,8 +479,51 @@
                 }
                 iterNum++;
             }
-            usagesAndLabels.put(start,
-                    proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
+
+            // TODO ntmyren: remove this proxy logic once camera is converted to AttributionSource
+            // For now: don't add mic proxy usages
+            if (!start.op.equals(OPSTR_RECORD_AUDIO)) {
+                usagesAndLabels.put(start,
+                        proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
+            }
+        }
+
+        for (int i = 0; i < mAttributionChains.size(); i++) {
+            List<AccessChainLink> usageList = mAttributionChains.valueAt(i);
+            int lastVisible = usageList.size() - 1;
+            // TODO ntmyren: remove this mic code once camera is converted to AttributionSource
+            // if the list is empty or incomplete, do not show it.
+            if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
+                    || !usageList.get(0).isStart()
+                    || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
+                continue;
+            }
+
+            //TODO ntmyren: remove once camera etc. etc.
+            for (AccessChainLink link: usageList) {
+                proxyPackages.add(link.usage.getPackageIdHash());
+            }
+
+            AccessChainLink start = usageList.get(0);
+            AccessChainLink lastVisibleLink = usageList.get(lastVisible);
+            while (lastVisible > 0 && !shouldShowPackage(lastVisibleLink.usage.packageName)) {
+                lastVisible--;
+                lastVisibleLink = usageList.get(lastVisible);
+            }
+            String proxyLabel = null;
+            if (!lastVisibleLink.usage.packageName.equals(start.usage.packageName)) {
+                try {
+                    PackageManager userPkgManager =
+                            getUserContext(lastVisibleLink.usage.getUser()).getPackageManager();
+                    ApplicationInfo appInfo = userPkgManager.getApplicationInfo(
+                            lastVisibleLink.usage.packageName, 0);
+                    proxyLabel = appInfo.loadLabel(userPkgManager).toString();
+                } catch (PackageManager.NameNotFoundException e) {
+                    // do nothing
+                }
+
+            }
+            usagesAndLabels.put(start.usage, proxyLabel);
         }
 
         for (int packageHash : mostRecentUsages.keySet()) {
@@ -495,4 +587,24 @@
                     && lastAccessTime == other.lastAccessTime && isRunning == other.isRunning;
         }
     }
+
+    private static class AccessChainLink {
+        public final OpUsage usage;
+        public final @AttributionFlags int flags;
+
+        AccessChainLink(String op, String packageName, String attributionTag, int uid,
+                int flags) {
+            this.usage = new OpUsage(packageName, attributionTag, op, uid,
+                    System.currentTimeMillis(), true, null);
+            this.flags = flags;
+        }
+
+        public boolean isEnd() {
+            return (flags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+        }
+
+        public boolean isStart() {
+            return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+        }
+    }
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 6a2b723..61f524f 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -154,4 +154,13 @@
      *                      app.
      */
     void detachNavigationBarFromApp(boolean moveHomeToTop);
+
+    /**
+     * Used for animating the navigation bar during app launch from recents in live tile mode.
+     *
+     * First fade out the navigation bar at the bottom of the display and then fade in to the app.
+     *
+     * @param duration the duration of the app launch animation
+     */
+    void animateNavigationBarToApp(long duration);
 }
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 40cce7c..d596626 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -44,6 +44,7 @@
 import java.time.Instant;
 import java.time.LocalTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Formatter;
 import java.util.Locale;
 
@@ -451,7 +452,9 @@
         if (mSecondHandTintInfo.mHasTintList || mSecondHandTintInfo.mHasTintBlendMode) {
             mSecondHand = mSecondHandTintInfo.apply(mSecondHand);
         }
-        mSecondsTick.run();
+        // Re-run the tick runnable immediately as the presence or absence of a seconds hand affects
+        // the next time we need to tick the clock.
+        mTick.run();
 
         mChanged = true;
         invalidate();
@@ -583,10 +586,10 @@
             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
 
             // OK, this is gross but needed. This class is supported by the
-            // remote views machanism and as a part of that the remote views
+            // remote views mechanism and as a part of that the remote views
             // can be inflated by a context for another user without the app
             // having interact users permission - just for loading resources.
-            // For exmaple, when adding widgets from a user profile to the
+            // For example, when adding widgets from a user profile to the
             // home screen. Therefore, we register the receiver as the current
             // user not the one the context is for.
             getContext().registerReceiverAsUser(mIntentReceiver,
@@ -616,14 +619,14 @@
     private void onVisible() {
         if (!mVisible) {
             mVisible = true;
-            mSecondsTick.run();
+            mTick.run();
         }
 
     }
 
     private void onInvisible() {
         if (mVisible) {
-            removeCallbacks(mSecondsTick);
+            removeCallbacks(mTick);
             mVisible = false;
         }
     }
@@ -760,6 +763,7 @@
         }
     }
 
+    /** Intent receiver for the time or time zone changing. */
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -767,36 +771,56 @@
                 createClock();
             }
 
-            onTimeChanged();
-
-            invalidate();
+            mTick.run();
         }
     };
     private boolean mReceiverAttached;
 
-    private final Runnable mSecondsTick = new Runnable() {
+    private final Runnable mTick = new Runnable() {
         @Override
         public void run() {
             removeCallbacks(this);
-            if (!mVisible || mSecondHand == null) {
+            if (!mVisible) {
                 return;
             }
 
             Instant now = mClock.instant();
-            LocalTime localTime = now.atZone(mClock.getZone()).toLocalTime();
-            // How many milliseconds through the second we currently are.
-            long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis();
-            // How many milliseconds there are between tick positions for the seconds hand.
-            double millisPerTick = 1000 / (double) mSecondsHandFps;
-            // How many milliseconds we are past the last tick position.
-            long millisPastLastTick = Math.round(millisOfSecond % millisPerTick);
-            // How many milliseconds there are until the next tick position.
-            long millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick);
-            // If we are exactly at the tick position, this could be 0 milliseconds due to rounding.
-            // In this case, advance by the full amount of millis to the next position.
-            if (millisUntilNextTick <= 0) {
-                millisUntilNextTick = Math.round(millisPerTick);
+            ZonedDateTime zonedDateTime = now.atZone(mClock.getZone());
+            LocalTime localTime = zonedDateTime.toLocalTime();
+
+            long millisUntilNextTick;
+            if (mSecondHand == null) {
+                // If there's no second hand, then tick at the start of the next minute.
+                //
+                // This must be done with ZonedDateTime as opposed to LocalDateTime to ensure proper
+                // handling of DST. Also note that because of leap seconds, it should not be assumed
+                // that one minute == 60 seconds.
+                Instant startOfNextMinute = zonedDateTime.plusMinutes(1).withSecond(0).toInstant();
+                millisUntilNextTick = Duration.between(now, startOfNextMinute).toMillis();
+                if (millisUntilNextTick <= 0) {
+                    // This should never occur, but if it does, then just check the tick again in
+                    // one minute to ensure we're always moving forward.
+                    millisUntilNextTick = Duration.ofMinutes(1).toMillis();
+                }
+            } else {
+                // If there is a seconds hand, then determine the next tick point based on the fps.
+                //
+                // How many milliseconds through the second we currently are.
+                long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis();
+                // How many milliseconds there are between tick positions for the seconds hand.
+                double millisPerTick = 1000 / (double) mSecondsHandFps;
+                // How many milliseconds we are past the last tick position.
+                long millisPastLastTick = Math.round(millisOfSecond % millisPerTick);
+                // How many milliseconds there are until the next tick position.
+                millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick);
+                // If we are exactly at the tick position, this could be 0 milliseconds due to
+                // rounding. In this case, advance by the full amount of millis to the next
+                // position.
+                if (millisUntilNextTick <= 0) {
+                    millisUntilNextTick = Math.round(millisPerTick);
+                }
             }
+
             // Schedule a callback for when the next tick should occur.
             postDelayed(this, millisUntilNextTick);
 
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index b1485ef..e48afb2 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -34,7 +34,6 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.format.DateFormat;
@@ -45,6 +44,10 @@
 
 import com.android.internal.R;
 
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Calendar;
 import java.util.TimeZone;
 
@@ -185,18 +188,29 @@
 
     private final Runnable mTicker = new Runnable() {
         public void run() {
-            if (mStopTicking) {
+            removeCallbacks(this);
+            if (mStopTicking || !mShouldRunTicker) {
                 return; // Test disabled the clock ticks
             }
             onTimeChanged();
 
-            long now = SystemClock.uptimeMillis();
-            long next = now + (1000 - now % 1000);
+            Instant now = mTime.toInstant();
+            ZoneId zone = mTime.getTimeZone().toZoneId();
 
-            Handler handler = getHandler();
-            if (handler != null) {
-                handler.postAtTime(mTicker, next);
+            ZonedDateTime nextTick;
+            if (mHasSeconds) {
+                nextTick = now.atZone(zone).plusSeconds(1).withNano(0);
+            } else {
+                nextTick = now.atZone(zone).plusMinutes(1).withSecond(0).withNano(0);
             }
+
+            long millisUntilNextTick = Duration.between(now, nextTick.toInstant()).toMillis();
+            if (millisUntilNextTick <= 0) {
+                // This should never happen, but if it does, then tick again in a second.
+                millisUntilNextTick = 1000;
+            }
+
+            postDelayed(this, millisUntilNextTick);
         }
     };
 
@@ -519,8 +533,7 @@
         mHasSeconds = DateFormat.hasSeconds(mFormat);
 
         if (mShouldRunTicker && hadSeconds != mHasSeconds) {
-            if (hadSeconds) getHandler().removeCallbacks(mTicker);
-            else mTicker.run();
+            mTicker.run();
         }
     }
 
@@ -557,14 +570,10 @@
 
         if (!mShouldRunTicker && isVisible) {
             mShouldRunTicker = true;
-            if (mHasSeconds) {
-                mTicker.run();
-            } else {
-                onTimeChanged();
-            }
+            mTicker.run();
         } else if (mShouldRunTicker && !isVisible) {
             mShouldRunTicker = false;
-            getHandler().removeCallbacks(mTicker);
+            removeCallbacks(mTicker);
         }
     }
 
@@ -592,7 +601,6 @@
     private void registerReceiver() {
         final IntentFilter filter = new IntentFilter();
 
-        filter.addAction(Intent.ACTION_TIME_TICK);
         filter.addAction(Intent.ACTION_TIME_CHANGED);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a64129e..756425e 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -293,4 +293,7 @@
          be able to connect to the internet when such a proxy is in use, since
          all outgoing connections originate from this app. -->
     <allow-in-power-save-except-idle package="com.android.proxyhandler" />
+
+    <!-- Allow IMS service entitlement app to schedule jobs to run when app in background. -->
+    <allow-in-power-save-except-idle package="com.android.imsserviceentitlement" />
 </permissions>
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 10df726..8881be7 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -19,7 +19,7 @@
     package="com.android.wm.shell">
     <!-- System permission required by WM Shell Task Organizer. -->
     <uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
 </manifest>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index a88be31..f7fb63d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -46,6 +46,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.sizecompatui.SizeCompatUIController;
 import com.android.wm.shell.startingsurface.StartingWindowController;
@@ -55,6 +56,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 /**
  * Unified task organizer for all components in the shell.
@@ -342,6 +344,19 @@
         notifySizeCompatUI(info.getTaskInfo(), listener);
     }
 
+    /**
+     * Take a screenshot of a task.
+     */
+    public void screenshotTask(RunningTaskInfo taskInfo, Rect crop,
+            Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+        final TaskAppearedInfo info = mTasks.get(taskInfo.taskId);
+        if (info == null) {
+            return;
+        }
+        ScreenshotUtils.captureLayer(info.getLeash(), crop, consumer);
+    }
+
+
     @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
         synchronized (mLock) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 2fc696c..35a4f33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -231,7 +231,8 @@
      * Fade animation for consecutive flyouts.
      */
     void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos,
-            boolean hideDot) {
+            boolean hideDot, Runnable onHide) {
+        mOnHide = onHide;
         final Runnable afterFadeOut = () -> {
             updateFlyoutMessage(flyoutMessage, parentWidth);
             // Wait for TextViews to layout with updated height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index d821c6f..8613dcb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1492,22 +1492,22 @@
             mStackAnimationController.setStackPosition(mPositioner.getDefaultStartPosition());
         }
 
-        if (getBubbleCount() == 0) {
-            mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
-        }
-
         if (bubble.getIconView() == null) {
             return;
         }
 
+        mBubbleContainer.addView(bubble.getIconView(), 0,
+                new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
+                        mPositioner.getBubbleSize()));
+
+        if (getBubbleCount() == 0) {
+            mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+        }
         // Set the dot position to the opposite of the side the stack is resting on, since the stack
         // resting slightly off-screen would result in the dot also being off-screen.
         bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
         bubble.getIconView().setOnClickListener(mBubbleClickListener);
         bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
-        mBubbleContainer.addView(bubble.getIconView(), 0,
-                new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
-                        mPositioner.getBubbleSize()));
         updateBubbleShadows(false /* showForAllBubbles */);
         animateInFlyoutForBubble(bubble);
         requestUpdate();
@@ -2400,7 +2400,8 @@
 
             if (mFlyout.getVisibility() == View.VISIBLE) {
                 mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
-                        mStackAnimationController.getStackPosition(), !bubble.showDot());
+                        mStackAnimationController.getStackPosition(), !bubble.showDot(),
+                        mAfterFlyoutHidden /* onHide */);
             } else {
                 mFlyout.setVisibility(INVISIBLE);
                 mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
@@ -2408,7 +2409,7 @@
                         mStackAnimationController.isStackOnLeftSide(),
                         bubble.getIconView().getDotColor() /* dotColor */,
                         expandFlyoutAfterDelay /* onLayoutComplete */,
-                        mAfterFlyoutHidden,
+                        mAfterFlyoutHidden /* onHide */,
                         bubble.getIconView().getDotCenter(),
                         !bubble.showDot(),
                         mPositioner);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 8043d2b..12d55b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -124,9 +124,6 @@
      */
     private Rect mAnimatingToBounds = new Rect();
 
-    /** Initial starting location for the stack. */
-    @Nullable private BubbleStackView.RelativeStackPosition mStackStartPosition;
-
     /** Whether or not the stack's start position has been set. */
     private boolean mStackMovedToStartPosition = false;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index 32575fa..eea6e3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -21,6 +21,8 @@
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
+import java.util.function.Consumer;
+
 /**
  * Helpers for working with screenshots.
  */
@@ -29,6 +31,60 @@
     /**
      * Take a screenshot of the specified SurfaceControl.
      *
+     * @param sc the SurfaceControl to take a screenshot of
+     * @param crop the crop to use when capturing the screenshot
+     * @param consumer Consumer for the captured buffer
+     */
+    public static void captureLayer(SurfaceControl sc, Rect crop,
+            Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+        consumer.accept(SurfaceControl.captureLayers(
+                new SurfaceControl.LayerCaptureArgs.Builder(sc)
+                    .setSourceCrop(crop)
+                    .setCaptureSecureLayers(true)
+                    .setAllowProtected(true)
+                    .build()));
+    }
+
+    private static class BufferConsumer implements
+            Consumer<SurfaceControl.ScreenshotHardwareBuffer> {
+        SurfaceControl mScreenshot = null;
+        SurfaceControl.Transaction mTransaction;
+        SurfaceControl mSurfaceControl;
+        int mLayer;
+
+        BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) {
+            mTransaction = t;
+            mSurfaceControl = sc;
+            mLayer = layer;
+        }
+
+        @Override
+        public void accept(SurfaceControl.ScreenshotHardwareBuffer buffer) {
+            if (buffer == null || buffer.getHardwareBuffer() == null) {
+                return;
+            }
+            final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+                buffer.getHardwareBuffer());
+            mScreenshot = new SurfaceControl.Builder()
+                .setName("ScreenshotUtils screenshot")
+                .setFormat(PixelFormat.TRANSLUCENT)
+                .setSecure(buffer.containsSecureLayers())
+                .setCallsite("ScreenshotUtils.takeScreenshot")
+                .setBLASTLayer()
+                .build();
+
+            mTransaction.setBuffer(mScreenshot, graphicBuffer);
+            mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
+            mTransaction.reparent(mScreenshot, mSurfaceControl);
+            mTransaction.setLayer(mScreenshot, mLayer);
+            mTransaction.show(mScreenshot);
+            mTransaction.apply();
+        }
+    }
+
+    /**
+     * Take a screenshot of the specified SurfaceControl.
+     *
      * @param t the transaction used to set changes on the resulting screenshot.
      * @param sc the SurfaceControl to take a screenshot of
      * @param crop the crop to use when capturing the screenshot
@@ -38,32 +94,8 @@
      */
     public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
             Rect crop, int layer) {
-        final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
-                new SurfaceControl.LayerCaptureArgs.Builder(sc)
-                        .setSourceCrop(crop)
-                        .setCaptureSecureLayers(true)
-                        .setAllowProtected(true)
-                        .build()
-        );
-        if (buffer == null || buffer.getHardwareBuffer() == null) {
-            return null;
-        }
-        final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
-                buffer.getHardwareBuffer());
-        final SurfaceControl screenshot = new SurfaceControl.Builder()
-                .setName("ScreenshotUtils screenshot")
-                .setFormat(PixelFormat.TRANSLUCENT)
-                .setSecure(buffer.containsSecureLayers())
-                .setCallsite("ScreenshotUtils.takeScreenshot")
-                .setBLASTLayer()
-                .build();
-
-        t.setBuffer(screenshot, graphicBuffer);
-        t.setColorSpace(screenshot, buffer.getColorSpace());
-        t.reparent(screenshot, sc);
-        t.setLayer(screenshot, layer);
-        t.show(screenshot);
-        t.apply();
-        return screenshot;
+        BufferConsumer consumer = new BufferConsumer(t, sc, layer);
+        captureLayer(sc, crop, consumer);
+        return consumer.mScreenshot;
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 7d032ad..afd7d26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -628,8 +628,10 @@
         final Rect destinationBounds = mPipBoundsState.getBounds();
         final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
-        mSurfaceTransactionHelper.resetScale(tx, mLeash, destinationBounds);
-        mSurfaceTransactionHelper.crop(tx, mLeash, destinationBounds);
+        mSurfaceTransactionHelper
+                .resetScale(tx, mLeash, destinationBounds)
+                .crop(tx, mLeash, destinationBounds)
+                .round(tx, mLeash, isInPip());
         // The animation is finished in the Launcher and here we directly apply the final touch.
         applyEnterPipSyncTransaction(destinationBounds, () -> {
             // Ensure menu's settled in its final bounds first.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 51a67e2..bf1f9e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -60,6 +60,7 @@
 import java.util.function.Consumer;
 import java.util.function.IntSupplier;
 import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
 
 /**
  * Util class to create the view for a splash screen content.
@@ -207,6 +208,15 @@
                 .build();
     }
 
+    private static <T> T safeReturnAttrDefault(UnaryOperator<T> getMethod, T def) {
+        try {
+            return getMethod.apply(def);
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Get attribute fail, return default: " + e.getMessage());
+            return def;
+        }
+    }
+
     /**
      * Get the {@link SplashScreenWindowAttrs} from {@code context} and fill them into
      * {@code attrs}.
@@ -215,16 +225,18 @@
         final TypedArray typedArray = context.obtainStyledAttributes(
                 com.android.internal.R.styleable.Window);
         attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
-        attrs.mWindowBgColor = typedArray.getColor(
-                R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT);
-        attrs.mReplaceIcon = typedArray.getDrawable(
-                R.styleable.Window_windowSplashScreenAnimatedIcon);
-        attrs.mAnimationDuration = typedArray.getInt(
-                R.styleable.Window_windowSplashScreenAnimationDuration, 0);
-        attrs.mBrandingImage = typedArray.getDrawable(
-                R.styleable.Window_windowSplashScreenBrandingImage);
-        attrs.mIconBgColor = typedArray.getColor(
-                R.styleable.Window_windowSplashScreenIconBackgroundColor, Color.TRANSPARENT);
+        attrs.mWindowBgColor = safeReturnAttrDefault((def) -> typedArray.getColor(
+                R.styleable.Window_windowSplashScreenBackground, def),
+                Color.TRANSPARENT);
+        attrs.mReplaceIcon = safeReturnAttrDefault((def) -> typedArray.getDrawable(
+                R.styleable.Window_windowSplashScreenAnimatedIcon), null);
+        attrs.mAnimationDuration = safeReturnAttrDefault((def) -> typedArray.getInt(
+                R.styleable.Window_windowSplashScreenAnimationDuration, def), 0);
+        attrs.mBrandingImage = safeReturnAttrDefault((def) -> typedArray.getDrawable(
+                R.styleable.Window_windowSplashScreenBrandingImage), null);
+        attrs.mIconBgColor = safeReturnAttrDefault((def) -> typedArray.getColor(
+                R.styleable.Window_windowSplashScreenIconBackgroundColor, def),
+                Color.TRANSPARENT);
         typedArray.recycle();
         if (DEBUG) {
             Slog.d(TAG, "window attributes color: "
@@ -476,9 +488,14 @@
                     drawable = layerDrawable.getDrawable(0);
                 }
             }
-            mColorChecker = drawable instanceof ColorDrawable
-                    ? new SingleColorTester((ColorDrawable) drawable)
-                    : new ComplexDrawableTester(drawable, filterTransparent);
+            if (drawable == null) {
+                mColorChecker = new SingleColorTester(
+                        (ColorDrawable) createDefaultBackgroundDrawable());
+            } else {
+                mColorChecker = drawable instanceof ColorDrawable
+                        ? new SingleColorTester((ColorDrawable) drawable)
+                        : new ComplexDrawableTester(drawable, filterTransparent);
+            }
         }
 
         public float nonTransparentRatio() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
index fbbd09f..ad9dda6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
@@ -16,6 +16,13 @@
 
 package com.android.wm.shell.tasksurfacehelper;
 
+import android.app.ActivityManager.RunningTaskInfo;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
 /**
  * Interface to communicate with a Task's SurfaceControl.
  */
@@ -23,4 +30,8 @@
 
     /** Sets the METADATA_GAME_MODE for the layer corresponding to the task **/
     default void setGameModeForTask(int taskId, int gameMode) {}
+
+    /** Takes a screenshot for a task **/
+    default void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor,
+            Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {}
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
index b459b9f..064d9d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
@@ -16,11 +16,16 @@
 
 package com.android.wm.shell.tasksurfacehelper;
 
+import android.app.ActivityManager.RunningTaskInfo;
+import android.graphics.Rect;
 import android.view.SurfaceControl;
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.ShellExecutor;
 
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
 /**
  * Intermediary controller that communicates with {@link ShellTaskOrganizer} to send commands
  * to SurfaceControl.
@@ -49,6 +54,14 @@
         mTaskOrganizer.setSurfaceMetadata(taskId, SurfaceControl.METADATA_GAME_MODE, gameMode);
     }
 
+    /**
+     * Take screenshot of the specified task.
+     */
+    public void screenshotTask(RunningTaskInfo taskInfo, Rect crop,
+            Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+        mTaskOrganizer.screenshotTask(taskInfo, crop, consumer);
+    }
+
     private class TaskSurfaceHelperImpl implements TaskSurfaceHelper {
         @Override
         public void setGameModeForTask(int taskId, int gameMode) {
@@ -56,5 +69,14 @@
                 TaskSurfaceHelperController.this.setGameModeForTask(taskId, gameMode);
             });
         }
+
+        @Override
+        public void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor,
+                Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+            mMainExecutor.execute(() -> {
+                TaskSurfaceHelperController.this.screenshotTask(taskInfo, crop,
+                        (t) -> executor.execute(() -> consumer.accept(t)));
+            });
+        }
     }
 }
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 01a2ec5..fe9a30a 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -429,7 +429,9 @@
                 kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
         if (bufferInfo->skSurface.get() == nullptr) {
             ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
-            mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
+            mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
+                                        mNativeBuffers[idx].dequeue_fence.release());
+            mNativeBuffers[idx].dequeued = false;
             return nullptr;
         }
     }
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index cb56ee5..a3842a1 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -178,6 +178,7 @@
     public static final int POWER_HIGH = 203;
 
     private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1;
+    private static final double IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR = 1D / 6D;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
             + "LocationManager} methods to provide the provider explicitly.")
@@ -552,7 +553,7 @@
      */
     public @IntRange(from = 0) long getMinUpdateIntervalMillis() {
         if (mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
-            return mInterval;
+            return (long) (mInterval * IMPLICIT_MIN_UPDATE_INTERVAL_FACTOR);
         } else {
             // the min is only necessary in case someone use a deprecated function to mess with the
             // interval or min update interval
@@ -1018,7 +1019,9 @@
          * Sets an explicit minimum update interval. If location updates are available faster than
          * the request interval then an update will only occur if the minimum update interval has
          * expired since the last location update. Defaults to no explicit minimum update interval
-         * set, which means the minimum update interval is the same as the interval.
+         * set, which means some sensible default between 0 and the interval will be chosen. The
+         * exact value is not specified at the moment. If an exact known value is required, clients
+         * should set an explicit value themselves.
          *
          * <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the
          * minimum update interval, so you need not worry about updates blocked simply because they
@@ -1037,7 +1040,8 @@
 
         /**
          * Clears an explicitly set minimum update interval and reverts to an implicit minimum
-         * update interval (ie, the minimum update interval is the same value as the interval).
+         * update interval (ie, the minimum update interval is some sensible default between 0 and
+         * the interval).
          */
         public @NonNull Builder clearMinUpdateIntervalMillis() {
             mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
index a39e779..e74ac44 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
@@ -33,7 +33,7 @@
     <style name="Banner.Title.SettingsLib"
         parent="@android:style/TextAppearance.Material.Subhead">
         <item name="android:textSize">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index 579abbd5..23aa993 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -42,7 +42,7 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:singleLine="true"
+            android:maxLines="2"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index 2c2756b..2c35772 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -40,7 +40,7 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:singleLine="true"
+            android:maxLines="2"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
 
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index f3f43ac..802d604 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -64,7 +64,7 @@
                 android:id="@android:id/title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:singleLine="true"
+                android:maxLines="2"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index e4f7242..f512f9b 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -61,7 +61,7 @@
                 android:id="@android:id/title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:singleLine="true"
+                android:maxLines="2"
                 android:textAppearance="?android:attr/textAppearanceListItem"
                 android:ellipsize="marquee" />
 
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index d9f91de..169ae97 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -51,7 +51,7 @@
         <TextView android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:singleLine="true"
+            android:maxLines="2"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee" />
 
diff --git a/packages/SettingsLib/res/layout/restricted_switch_widget.xml b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
index 5dbcb79..1520ac8 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_widget.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_widget.xml
@@ -16,11 +16,11 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/restricted_icon"
-        android:layout_width="@*android:dimen/config_restrictedIconSize"
+        android:layout_width="@dimen/two_target_min_width"
         android:layout_height="@*android:dimen/config_restrictedIconSize"
         android:tint="?android:attr/colorAccent"
         android:src="@*android:drawable/ic_info"
-        android:gravity="end|center_vertical" />
+        android:gravity="center" />
     <!-- Based off frameworks/base/core/res/res/layout/preference_widget_switch.xml -->
     <Switch xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@android:id/switch_widget"
diff --git a/packages/SystemUI/res/drawable/people_space_activity_card.xml b/packages/SystemUI/res/drawable/people_space_activity_card.xml
index 338bff8..7e2db63 100644
--- a/packages/SystemUI/res/drawable/people_space_activity_card.xml
+++ b/packages/SystemUI/res/drawable/people_space_activity_card.xml
@@ -14,5 +14,5 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <solid android:color="@color/people_tile_background" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml
index 30519ae..9704871 100644
--- a/packages/SystemUI/res/drawable/people_space_content_background.xml
+++ b/packages/SystemUI/res/drawable/people_space_content_background.xml
@@ -15,6 +15,6 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
-    <solid android:color="?android:attr/colorBackground" />
+    <solid android:color="@color/people_tile_background" />
     <corners android:radius="@dimen/people_space_image_radius" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/people_space_new_story_outline.xml b/packages/SystemUI/res/drawable/people_space_new_story_outline.xml
index a1737f9..4d6249d 100644
--- a/packages/SystemUI/res/drawable/people_space_new_story_outline.xml
+++ b/packages/SystemUI/res/drawable/people_space_new_story_outline.xml
@@ -15,6 +15,6 @@
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
-    <solid android:color="?android:attr/colorBackground" />
+    <solid android:color="@color/people_tile_background" />
     <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_space_rounded_border.xml b/packages/SystemUI/res/drawable/people_space_rounded_border.xml
index 9956bc2..50560df 100644
--- a/packages/SystemUI/res/drawable/people_space_rounded_border.xml
+++ b/packages/SystemUI/res/drawable/people_space_rounded_border.xml
@@ -15,5 +15,5 @@
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
-    <solid android:color="?android:attr/colorBackground" />
+    <solid android:color="@color/people_tile_background" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_space_tile_view_card.xml b/packages/SystemUI/res/drawable/people_space_tile_view_card.xml
index 8fd4388..6e1e5de 100644
--- a/packages/SystemUI/res/drawable/people_space_tile_view_card.xml
+++ b/packages/SystemUI/res/drawable/people_space_tile_view_card.xml
@@ -14,6 +14,6 @@
   ~ limitations under the License.
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="?android:attr/colorBackground" />
+    <solid android:color="@color/people_tile_background" />
     <corners android:radius="@dimen/people_space_widget_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_empty_background.xml b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
index 2dac740..7dbea73 100644
--- a/packages/SystemUI/res/drawable/people_tile_empty_background.xml
+++ b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
@@ -14,8 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <solid android:color="?androidprv:attr/colorSurface" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/people_tile_background" />
     <corners android:radius="@dimen/people_space_widget_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index c473229..8e6293a 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -97,4 +97,6 @@
 
     <!-- Accessibility floating menu -->
     <color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
+
+    <color name="people_tile_background">@android:color/system_accent2_800</color>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f64299d..2cf3058 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -281,4 +281,6 @@
 
     <!-- Wallet screen -->
     <color name="wallet_card_border">#33FFFFFF</color>
+
+    <color name="people_tile_background">@android:color/system_accent2_50</color>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 74aa138..fb9aa4a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -46,12 +46,13 @@
         mTmpSourceRectF.set(sourceBounds);
         mTmpDestinationRectF.set(destinationBounds);
         mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+        final float cornerRadius = getScaledCornerRadius(sourceBounds, destinationBounds);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top)
-                .setCornerRadius(leash, mCornerRadius);
+                .setCornerRadius(leash, cornerRadius);
         return new PictureInPictureSurfaceTransaction(
                 mTmpDestinationRectF.left, mTmpDestinationRectF.top,
-                mTmpFloat9, 0 /* rotation */, mCornerRadius, sourceBounds);
+                mTmpFloat9, 0 /* rotation */, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scale(
@@ -62,11 +63,12 @@
         mTmpDestinationRectF.set(destinationBounds);
         mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
         mTmpTransform.postRotate(degree, 0, 0);
+        final float cornerRadius = getScaledCornerRadius(sourceBounds, destinationBounds);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setPosition(leash, positionX, positionY)
-                .setCornerRadius(leash, mCornerRadius);
+                .setCornerRadius(leash, cornerRadius);
         return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, degree, mCornerRadius, sourceBounds);
+                positionX, positionY, mTmpFloat9, degree, cornerRadius, sourceBounds);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndCrop(
@@ -83,12 +85,15 @@
         final float left = destinationBounds.left - insets.left * scale;
         final float top = destinationBounds.top - insets.top * scale;
         mTmpTransform.setScale(scale, scale);
+        final Rect cornerRadiusRect = new Rect(destinationBounds);
+        cornerRadiusRect.inset(insets);
+        final float cornerRadius = getScaledCornerRadius(sourceBounds, cornerRadiusRect);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, left, top)
-                .setCornerRadius(leash, mCornerRadius);
+                .setCornerRadius(leash, cornerRadius);
         return new PictureInPictureSurfaceTransaction(
-                left, top, mTmpFloat9, 0 /* rotation */, mCornerRadius, mTmpDestinationRect);
+                left, top, mTmpFloat9, 0 /* rotation */, cornerRadius, mTmpDestinationRect);
     }
 
     public PictureInPictureSurfaceTransaction scaleAndRotate(
@@ -105,12 +110,22 @@
                 : (float) destinationBounds.height() / sourceBounds.height();
         mTmpTransform.setRotate(degree, 0, 0);
         mTmpTransform.postScale(scale, scale);
+        final Rect cornerRadiusRect = new Rect(destinationBounds);
+        cornerRadiusRect.inset(insets);
+        final float cornerRadius = getScaledCornerRadius(sourceBounds, cornerRadiusRect);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setWindowCrop(leash, mTmpDestinationRect)
                 .setPosition(leash, positionX, positionY)
-                .setCornerRadius(leash, mCornerRadius);
+                .setCornerRadius(leash, cornerRadius);
         return new PictureInPictureSurfaceTransaction(
-                positionX, positionY, mTmpFloat9, degree, mCornerRadius, mTmpDestinationRect);
+                positionX, positionY, mTmpFloat9, degree, cornerRadius, mTmpDestinationRect);
+    }
+
+    /** @return the round corner radius scaled by given from and to bounds */
+    private float getScaledCornerRadius(Rect fromBounds, Rect toBounds) {
+        final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height())
+                / Math.hypot(toBounds.width(), toBounds.height()));
+        return mCornerRadius * scale;
     }
 
     /** @return {@link SurfaceControl.Transaction} instance with vsync-id */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 700ec49..8e65560 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -152,4 +152,15 @@
             Log.e(TAG, "Failed to detach the navigation bar from app", e);
         }
     }
+
+    /**
+     * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+     */
+    public void animateNavigationBarToApp(long duration) {
+        try {
+            mAnimationController.animateNavigationBarToApp(duration);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to animate the navigation bar to app", e);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 619bf1b..92ffb42 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -64,9 +64,10 @@
     private float mDarkAmount;
 
     /**
-     * Boolean value indicating if notifications are visible on lock screen.
+     * Boolean value indicating if notifications are visible on lock screen. Use null to signify
+     * it is uninitialized.
      */
-    private boolean mHasVisibleNotifications = true;
+    private Boolean mHasVisibleNotifications = null;
 
     private AnimatorSet mClockInAnim = null;
     private AnimatorSet mClockOutAnim = null;
@@ -263,7 +264,8 @@
      * the smaller version.
      */
     boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
-        if (hasVisibleNotifications == mHasVisibleNotifications) {
+        if (mHasVisibleNotifications != null
+                && hasVisibleNotifications == mHasVisibleNotifications) {
             return false;
         }
         boolean useLargeClock = !hasVisibleNotifications;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index fcb090a..717c715 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -66,6 +66,7 @@
 
 import java.io.PrintWriter;
 import java.text.NumberFormat;
+import java.util.Collections;
 import java.util.Locale;
 
 /**
@@ -178,7 +179,13 @@
         // Initialize listeners.
         mMirrorViewRunnable = () -> {
             if (mMirrorView != null) {
+                final Rect oldViewBounds = new Rect(mMirrorViewBounds);
                 mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
+                if (oldViewBounds.width() != mMirrorViewBounds.width()
+                        || oldViewBounds.height() != mMirrorViewBounds.height()) {
+                    mMirrorView.setSystemGestureExclusionRects(Collections.singletonList(
+                            new Rect(0, 0, mMirrorViewBounds.width(), mMirrorViewBounds.height())));
+                }
                 updateSystemUIStateIfNeeded();
                 mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
                         mDisplayId, mMirrorViewBounds);
@@ -269,6 +276,7 @@
         if (mMirrorWindowControl != null) {
             mMirrorWindowControl.destroyControl();
         }
+        mMirrorViewBounds.setEmpty();
         updateSystemUIStateIfNeeded();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
index ca59393..76fb49a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
@@ -130,19 +130,29 @@
     @Override
     public void onAuthenticationFailed(
             @Modality int modality, @Nullable String failureReason) {
-        if (modality == TYPE_FACE && mActiveSensorType == TYPE_FACE) {
-            // switching from face -> fingerprint mode, suppress soft error messages
-            failureReason = mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead);
+        super.onAuthenticationFailed(modality, checkErrorForFallback(failureReason));
+    }
+
+    @Override
+    public void onError(int modality, String error) {
+        super.onError(modality, checkErrorForFallback(error));
+    }
+
+    private String checkErrorForFallback(String message) {
+        if (mActiveSensorType == TYPE_FACE) {
+            Log.d(TAG, "Falling back to fingerprint: " + message);
+
+            // switching from face -> fingerprint mode, suppress root error messages
+            mCallback.onAction(Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR);
+            return mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead);
         }
-        super.onAuthenticationFailed(modality, failureReason);
+        return message;
     }
 
     @Override
     @BiometricState
     protected int getStateForAfterError() {
         if (mActiveSensorType == TYPE_FACE) {
-            mHandler.post(() -> mCallback.onAction(
-                    Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
             return STATE_AUTHENTICATING;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f7b3a35..085a076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -26,6 +26,8 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
@@ -53,6 +55,11 @@
     private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private static final String TAG = "NotificationShelf";
 
+    // More extreme version of SLOW_OUT_LINEAR_IN which keeps the icon nearly invisible until after
+    // the next icon has translated out of the way, to avoid overlapping.
+    private static final Interpolator ICON_ALPHA_INTERPOLATOR =
+            new PathInterpolator(0.6f, 0f, 0.6f, 0f);
+
     private NotificationIconContainer mShelfIcons;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
@@ -659,7 +666,7 @@
         if (iconState == null) {
             return;
         }
-        iconState.alpha = transitionAmount;
+        iconState.alpha = ICON_ALPHA_INTERPOLATOR.getInterpolation(transitionAmount);
         boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
         iconState.hidden = isAppearing
                 || (view instanceof ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9390c81..2c2a170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2097,11 +2097,13 @@
                 if (height != 0) {
                     height += mPaddingBetweenElements;
                 }
-                height += calculateGapHeight(previousView, expandableView, numShownItems);
+                float gapHeight = calculateGapHeight(previousView, expandableView, numShownNotifs);
+                height += gapHeight;
                 height += viewHeight;
 
                 numShownItems++;
-                if (!(expandableView instanceof MediaHeaderView)) {
+                if (viewHeight > 0 || !(expandableView instanceof MediaHeaderView)) {
+                    // Only count the media as a notification if it has a positive height.
                     numShownNotifs++;
                 }
                 previousView = expandableView;
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 fda24c1..e2d64b09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -139,6 +139,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -1325,21 +1326,27 @@
         ExpandableView previousView = null;
         for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
             ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i);
-            if (!(child instanceof ExpandableNotificationRow)) {
-                continue;
-            }
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            boolean
-                    suppressedSummary =
-                    mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
-                            row.getEntry().getSbn());
-            if (suppressedSummary) {
-                continue;
-            }
-            if (!canShowViewOnLockscreen(child)) {
-                continue;
-            }
-            if (row.isRemoved()) {
+            if (child instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                boolean suppressedSummary = mGroupManager != null
+                        && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn());
+                if (suppressedSummary) {
+                    continue;
+                }
+                if (!canShowViewOnLockscreen(child)) {
+                    continue;
+                }
+                if (row.isRemoved()) {
+                    continue;
+                }
+            } else if (child instanceof MediaHeaderView) {
+                if (child.getVisibility() == GONE) {
+                    continue;
+                }
+                if (child.getIntrinsicHeight() == 0) {
+                    continue;
+                }
+            } else {
                 continue;
             }
             availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index d61848c..e0ff88b 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -75,6 +75,10 @@
 
     static final String COLOR_SOURCE_PRESET = "preset";
 
+    static final String COLOR_SOURCE_HOME = "home_wallpaper";
+
+    static final String COLOR_SOURCE_LOCK = "lock_wallpaper";
+
     static final String TIMESTAMP_FIELD = "_applied_timestamp";
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c23e0b4..428921e 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -16,6 +16,8 @@
 package com.android.systemui.theme;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
+import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
@@ -200,36 +202,37 @@
         String overlayPackageJson = mSecureSettings.getStringForUser(
                 Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
                 currentUser);
-        boolean isDestinationBoth = mWallpaperManager.getWallpaperId(
-                WallpaperManager.FLAG_LOCK) < 0;
-        if (!TextUtils.isEmpty(overlayPackageJson)) {
-            try {
-                JSONObject jsonObject = new JSONObject(overlayPackageJson);
-                if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
-                        && ((flags & latestWallpaperType) != 0)) {
-                    mSkipSettingChange = true;
-                    if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
-                            OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
-                        jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
-                        jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
-                        jsonObject.remove(OVERLAY_COLOR_SOURCE);
-                        jsonObject.remove(OVERLAY_COLOR_INDEX);
-                    }
-                    // Keep color_both value because users can change either or both home and
-                    // lock screen wallpapers.
-                    jsonObject.put(OVERLAY_COLOR_BOTH, isDestinationBoth ? "1" : "0");
-
-                    jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
-                    if (DEBUG) {
-                        Log.d(TAG, "Updating theme setting from "
-                                + overlayPackageJson + " to " + jsonObject.toString());
-                    }
-                    mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
-                            jsonObject.toString());
+        boolean isDestinationBoth = (flags == (WallpaperManager.FLAG_SYSTEM
+                | WallpaperManager.FLAG_LOCK));
+        try {
+            JSONObject jsonObject = (overlayPackageJson == null) ? new JSONObject()
+                    : new JSONObject(overlayPackageJson);
+            if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
+                    && ((flags & latestWallpaperType) != 0)) {
+                mSkipSettingChange = true;
+                if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
+                        OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
+                    jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+                    jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
+                    jsonObject.remove(OVERLAY_COLOR_INDEX);
                 }
-            } catch (JSONException e) {
-                Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
+                // Keep color_both value because users can change either or both home and
+                // lock screen wallpapers.
+                jsonObject.put(OVERLAY_COLOR_BOTH, isDestinationBoth ? "1" : "0");
+
+                jsonObject.put(OVERLAY_COLOR_SOURCE,
+                        (flags == WallpaperManager.FLAG_LOCK) ? COLOR_SOURCE_LOCK
+                                : COLOR_SOURCE_HOME);
+                jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
+                if (DEBUG) {
+                    Log.d(TAG, "Updating theme setting from "
+                            + overlayPackageJson + " to " + jsonObject.toString());
+                }
+                mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+                        jsonObject.toString());
             }
+        } catch (JSONException e) {
+            Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
         }
         reevaluateSystemTheme(false /* forceReload */);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index ad99e4d..f62069d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -77,6 +77,8 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 @LargeTest
 @RunWith(AndroidTestingRunner.class)
 public class WindowMagnificationControllerTest extends SysuiTestCase {
@@ -152,6 +154,19 @@
     }
 
     @Test
+    public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        // Wait for Rects updated.
+        waitForIdleSync();
+
+        List<Rect> rects = mWindowManager.getAttachedView().getSystemGestureExclusionRects();
+        assertFalse(rects.isEmpty());
+    }
+
+    @Test
     public void deleteWindowMagnification_destroyControl() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index aed0da6..82bf041 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
@@ -124,16 +126,50 @@
     }
 
     @Test
-    public void testModeUpdated_whenSwitchToFingerprint() {
+    public void testModeUpdated_onSoftError_whenSwitchToFingerprint() {
         mFaceToFpView.onDialogAnimatedIn();
         mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
         waitForIdleSync();
 
         verify(mIndicatorView).setText(
                 eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
+        verify(mCallback).onAction(
+                eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
         assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
     }
 
+    @Test
+    public void testModeUpdated_onHardError_whenSwitchToFingerprint() {
+        mFaceToFpView.onDialogAnimatedIn();
+        mFaceToFpView.onError(TYPE_FACE, "oh no!");
+        waitForIdleSync();
+
+        verify(mIndicatorView).setText(
+                eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
+        verify(mCallback).onAction(
+                eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
+        assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+    }
+
+    @Test
+    public void testFingerprintOnlyStartsOnFirstError() {
+        mFaceToFpView.onDialogAnimatedIn();
+        verify(mFaceToFpView.mIconController)
+                .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+
+        mFaceToFpView.onDialogAnimatedIn();
+        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
+        mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
+
+        reset(mCallback);
+
+        mFaceToFpView.onError(TYPE_FACE, "oh no!");
+        mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
+
+        verify(mCallback, never()).onAction(
+                eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
+    }
+
     public class TestableView extends AuthBiometricFaceToFingerprintView {
         public TestableView(Context context) {
             super(context, null, new MockInjector());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 3d2a0f1..3cf9212 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -232,9 +232,9 @@
         verify(mSecureSettings).putString(
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
 
-        assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette"))
+        assertThat(updatedSetting.getValue().contains("android.theme.customization.accent_color"))
                 .isFalse();
-        assertThat(updatedSetting.getValue().contains("android.theme.customization.color_source"))
+        assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette"))
                 .isFalse();
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
@@ -289,7 +289,8 @@
                 .thenReturn(jsonString);
         when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(-1);
 
-        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+        mColorsListener.getValue().onColorsChanged(mainColors,
+                WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
 
         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
         verify(mSecureSettings).putString(
@@ -303,6 +304,60 @@
     }
 
     @Test
+    public void onWallpaperColorsChanged_changeLockWallpaper() {
+        // Should ask for a new theme when wallpaper colors change
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        String jsonString =
+                "{\"android.theme.customization.system_palette\":\"override.package.name\","
+                        + "\"android.theme.customization.color_source\":\"home_wallpaper\","
+                        + "\"android.theme.customization.color_index\":\"2\"}";
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+        when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(1);
+
+        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK);
+
+        ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+        verify(mSecureSettings).putString(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+        assertThat(updatedSetting.getValue().contains(
+                "android.theme.customization.color_source\":\"lock_wallpaper")).isTrue();
+        assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
+                .isFalse();
+        verify(mThemeOverlayApplier)
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+    @Test
+    public void onWallpaperColorsChanged_changeHomeWallpaper() {
+        // Should ask for a new theme when wallpaper colors change
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        String jsonString =
+                "{\"android.theme.customization.system_palette\":\"override.package.name\","
+                        + "\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                        + "\"android.theme.customization.color_index\":\"2\"}";
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+        when(mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK)).thenReturn(-1);
+
+        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+
+        ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+        verify(mSecureSettings).putString(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
+        assertThat(updatedSetting.getValue().contains(
+                "android.theme.customization.color_source\":\"home_wallpaper")).isTrue();
+        assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
+                .isFalse();
+        verify(mThemeOverlayApplier)
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+    @Test
     public void onWallpaperColorsChanged_ResetThemeWhenFromLatestWallpaper() {
         // Should ask for a new theme when the colors of the last applied wallpaper change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c994d7c..c841fa3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5996,13 +5996,23 @@
     public final ContentProviderHolder getContentProvider(
             IApplicationThread caller, String callingPackage, String name, int userId,
             boolean stable) {
-        return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
+        traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProvider: ", name);
+        try {
+            return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     @Override
     public ContentProviderHolder getContentProviderExternal(
             String name, int userId, IBinder token, String tag) {
-        return mCpHelper.getContentProviderExternal(name, userId, token, tag);
+        traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProviderExternal: ", name);
+        try {
+            return mCpHelper.getContentProviderExternal(name, userId, token, tag);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     /**
@@ -6017,18 +6027,57 @@
     @Deprecated
     @Override
     public void removeContentProviderExternal(String name, IBinder token) {
-        removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+        traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "removeContentProviderExternal: ", name);
+        try {
+            removeContentProviderExternalAsUser(name, token, UserHandle.getCallingUserId());
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     @Override
     public void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
-        mCpHelper.removeContentProviderExternalAsUser(name, token, userId);
+        traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "removeContentProviderExternalAsUser: ", name);
+        try {
+            mCpHelper.removeContentProviderExternalAsUser(name, token, userId);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     @Override
     public final void publishContentProviders(IApplicationThread caller,
             List<ContentProviderHolder> providers) {
-        mCpHelper.publishContentProviders(caller, providers);
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            final int maxLength = 256;
+            final StringBuilder sb = new StringBuilder(maxLength);
+            sb.append("publishContentProviders: ");
+            if (providers != null) {
+                boolean first = true;
+                for (int i = 0, size = providers.size(); i < size; i++) {
+                    final ContentProviderHolder holder = providers.get(i);
+                    if (holder != null && holder.info != null && holder.info.authority != null) {
+                        final int len = holder.info.authority.length();
+                        if (sb.length() + len > maxLength) {
+                            sb.append("[[TRUNCATED]]");
+                            break;
+                        }
+                        if (!first) {
+                            sb.append(';');
+                        } else {
+                            first = false;
+                        }
+                        sb.append(holder.info.authority);
+                    }
+                }
+            }
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, sb.toString());
+        }
+        try {
+            mCpHelper.publishContentProviders(caller, providers);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     @Override
@@ -17108,4 +17157,10 @@
             SystemClock.sleep(durationMs);
         }
     }
+
+    static void traceBegin(long traceTag, String methodName, String subInfo) {
+        if (Trace.isTagEnabled(traceTag)) {
+            Trace.traceBegin(traceTag, methodName + subInfo);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index ab1da80..1611395 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -58,6 +58,7 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -707,20 +708,28 @@
         mService.enforceNotIsolatedCaller("removeContentProvider");
         final long ident = Binder.clearCallingIdentity();
         try {
-            synchronized (mService) {
-                ContentProviderConnection conn;
-                try {
-                    conn = (ContentProviderConnection) connection;
-                } catch (ClassCastException e) {
-                    String msg = "removeContentProvider: " + connection
-                            + " not a ContentProviderConnection";
-                    Slog.w(TAG, msg);
-                    throw new IllegalArgumentException(msg);
+            ContentProviderConnection conn;
+            try {
+                conn = (ContentProviderConnection) connection;
+            } catch (ClassCastException e) {
+                String msg = "removeContentProvider: " + connection
+                        + " not a ContentProviderConnection";
+                Slog.w(TAG, msg);
+                throw new IllegalArgumentException(msg);
+            }
+            if (conn == null) {
+                throw new NullPointerException("connection is null");
+            }
+            ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                    "removeContentProvider: ",
+                    (conn.provider != null && conn.provider.info != null
+                    ? conn.provider.info.authority : ""));
+            try {
+                synchronized (mService) {
+                    decProviderCountLocked(conn, null, null, stable, true, true);
                 }
-                if (conn == null) {
-                    throw new NullPointerException("connection is null");
-                }
-                decProviderCountLocked(conn, null, null, stable, true, true);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -781,8 +790,15 @@
             throw new NullPointerException("connection is null");
         }
 
-        conn.adjustCounts(stable, unstable);
-        return !conn.dead;
+        ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "refContentProvider: ",
+                (conn.provider != null && conn.provider.info != null
+                ? conn.provider.info.authority : ""));
+        try {
+            conn.adjustCounts(stable, unstable);
+            return !conn.dead;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     void unstableProviderDied(IBinder connection) {
@@ -798,50 +814,60 @@
             throw new NullPointerException("connection is null");
         }
 
-        // Safely retrieve the content provider associated with the connection.
-        IContentProvider provider;
-        synchronized (mService) {
-            provider = conn.provider.provider;
-        }
+        ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "unstableProviderDied: ",
+                (conn.provider != null && conn.provider.info != null
+                ? conn.provider.info.authority : ""));
 
-        if (provider == null) {
-            // Um, yeah, we're way ahead of you.
-            return;
-        }
-
-        // Make sure the caller is being honest with us.
-        if (provider.asBinder().pingBinder()) {
-            // Er, no, still looks good to us.
+        try {
+            // Safely retrieve the content provider associated with the connection.
+            IContentProvider provider;
             synchronized (mService) {
-                Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
-                        + " says " + conn + " died, but we don't agree");
-                return;
+                provider = conn.provider.provider;
             }
-        }
 
-        // Well look at that!  It's dead!
-        synchronized (mService) {
-            if (conn.provider.provider != provider) {
-                // But something changed...  good enough.
+            if (provider == null) {
+                // Um, yeah, we're way ahead of you.
                 return;
             }
 
-            ProcessRecord proc = conn.provider.proc;
-            if (proc == null || proc.getThread() == null) {
-                // Seems like the process is already cleaned up.
-                return;
+            // Make sure the caller is being honest with us.
+            if (provider.asBinder().pingBinder()) {
+                // Er, no, still looks good to us.
+                synchronized (mService) {
+                    Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+                            + " says " + conn + " died, but we don't agree");
+                    return;
+                }
             }
 
-            // As far as we're concerned, this is just like receiving a
-            // death notification...  just a bit prematurely.
-            mService.reportUidInfoMessageLocked(TAG, "Process " + proc.processName
-                            + " (pid " + proc.getPid() + ") early provider death", proc.info.uid);
-            final long token = Binder.clearCallingIdentity();
-            try {
-                mService.appDiedLocked(proc, "unstable content provider");
-            } finally {
-                Binder.restoreCallingIdentity(token);
+            // Well look at that!  It's dead!
+            synchronized (mService) {
+                if (conn.provider.provider != provider) {
+                    // But something changed...  good enough.
+                    return;
+                }
+
+                ProcessRecord proc = conn.provider.proc;
+                if (proc == null || proc.getThread() == null) {
+                    // Seems like the process is already cleaned up.
+                    return;
+                }
+
+                // As far as we're concerned, this is just like receiving a
+                // death notification...  just a bit prematurely.
+                mService.reportUidInfoMessageLocked(TAG, "Process " + proc.processName
+                                + " (pid " + proc.getPid() + ") early provider death",
+                                proc.info.uid);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mService.appDiedLocked(proc, "unstable content provider");
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
     }
 
@@ -855,13 +881,21 @@
             return;
         }
 
-        final ProcessRecord host = conn.provider.proc;
-        if (host == null) {
-            Slog.w(TAG, "Failed to find hosting ProcessRecord");
-            return;
-        }
+        ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "appNotRespondingViaProvider: ",
+                (conn.provider != null && conn.provider.info != null
+                ? conn.provider.info.authority : ""));
+        try {
+            final ProcessRecord host = conn.provider.proc;
+            if (host == null) {
+                Slog.w(TAG, "Failed to find hosting ProcessRecord");
+                return;
+            }
 
-        mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
+            mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 55128be..c2259eb 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -84,8 +84,8 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.AppOpsManager.AttributionFlags;
 import android.app.AppOpsManager.AttributedOpEntry;
+import android.app.AppOpsManager.AttributionFlags;
 import android.app.AppOpsManager.HistoricalOps;
 import android.app.AppOpsManager.Mode;
 import android.app.AppOpsManager.OpEntry;
@@ -3255,13 +3255,6 @@
                 shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
     }
 
-    // TODO b/184963112: remove once full blaming is implemented
-    private boolean isRecognitionServiceTemp(int code, String packageName) {
-        return code == OP_RECORD_AUDIO
-                && (packageName.equals("com.google.android.googlequicksearchbox")
-                || packageName.equals("com.google.android.tts"));
-    }
-
     private SyncNotedAppOp noteProxyOperationImpl(int code, AttributionSource attributionSource,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             boolean skipProxyOperation) {
@@ -3289,8 +3282,7 @@
         final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isSelfBlame
-                || isRecognitionServiceTemp(code, proxyPackageName);
+                == PackageManager.PERMISSION_GRANTED || isSelfBlame;
 
         if (!skipProxyOperation) {
             final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 36dc5cd..ef02a47 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -355,10 +355,6 @@
         }
     }
 
-    private void cancelAllFingerprintSensors() {
-        cancelAllSensors(sensor -> sensor.modality == TYPE_FINGERPRINT);
-    }
-
     private void cancelAllSensors() {
         cancelAllSensors(sensor -> true);
     }
@@ -387,6 +383,9 @@
      */
     boolean onErrorReceived(int sensorId, int cookie, @BiometricConstants.Errors int error,
             int vendorCode) throws RemoteException {
+        if (DEBUG) {
+            Slog.v(TAG, "onErrorReceived sensor: " + sensorId + " error: " + error);
+        }
 
         if (!containsCookie(cookie)) {
             Slog.e(TAG, "Unknown/expired cookie: " + cookie);
@@ -454,12 +453,14 @@
                     // a round trip to SystemUI.
                     mClientReceiver.onError(modality, error, vendorCode);
                     return true;
-                } else if (shouldErrorTriggerMultiSensorTransition()) {
-                    // wait for the UI to signal when modality should switch
-                    mMultiSensorState = MULTI_SENSOR_STATE_SWITCHING;
-                    Slog.d(TAG, "onErrorReceived: waiting for modality switch callback");
                 } else {
                     mState = STATE_ERROR_PENDING_SYSUI;
+                    if (mMultiSensorMode == BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT
+                            && mMultiSensorState == MULTI_SENSOR_STATE_FACE_SCANNING) {
+                        // wait for the UI to signal when modality should switch
+                        Slog.d(TAG, "onErrorReceived: waiting for modality switch callback");
+                        mMultiSensorState = MULTI_SENSOR_STATE_SWITCHING;
+                    }
                     mStatusBarService.onBiometricError(modality, error, vendorCode);
                 }
                 break;
@@ -627,7 +628,7 @@
     void onDeviceCredentialPressed() {
         // Cancel authentication. Skip the token/package check since we are cancelling
         // from system server. The interface is permission protected so this is fine.
-        cancelBiometricOnly();
+        cancelAllSensors();
         mState = STATE_SHOWING_DEVICE_CREDENTIAL;
     }
 
@@ -733,12 +734,10 @@
                     }
                     mClientReceiver.onAuthenticationSucceeded(
                             Utils.getAuthenticationTypeForResult(reason));
-                    cancelBiometricOnly();
                     break;
 
                 case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
                     mClientReceiver.onDialogDismissed(reason);
-                    cancelBiometricOnly();
                     break;
 
                 case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
@@ -747,7 +746,6 @@
                             BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
                             0 /* vendorCode */
                     );
-                    cancelBiometricOnly();
                     break;
 
                 case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
@@ -765,6 +763,9 @@
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
+        } finally {
+            // ensure everything is cleaned up when dismissed
+            cancelAllSensors();
         }
     }
 
@@ -780,8 +781,8 @@
                 || mState == STATE_AUTH_STARTED
                 || mState == STATE_AUTH_STARTED_UI_SHOWING;
 
+        cancelAllSensors();
         if (authStarted && !force) {
-            cancelAllSensors();
             // Wait for ERROR_CANCELED to be returned from the sensors
             return false;
         } else {
@@ -804,22 +805,6 @@
         return false;
     }
 
-    /**
-     * Cancels biometric authentication only. AuthSession may either be going into
-     * {@link #STATE_SHOWING_DEVICE_CREDENTIAL} or dismissed.
-     */
-    private void cancelBiometricOnly() {
-        if (mState == STATE_AUTH_STARTED || mState == STATE_AUTH_STARTED_UI_SHOWING) {
-            cancelAllSensors();
-        } else if (mMultiSensorMode == BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT) {
-            cancelAllFingerprintSensors();
-        } else {
-            if (DEBUG)  {
-                Slog.v(TAG, "nothing to cancel - wrong state: " + mState);
-            }
-        }
-    }
-
     boolean isCrypto() {
         return mOperationId != 0;
     }
@@ -896,13 +881,6 @@
         }
     }
 
-    private boolean shouldErrorTriggerMultiSensorTransition() {
-        if (mMultiSensorMode == BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT) {
-            return mMultiSensorState == MULTI_SENSOR_STATE_FACE_SCANNING;
-        }
-        return false;
-    }
-
     @BiometricMultiSensorMode
     private static int getMultiSensorModeForNewSession(Collection<BiometricSensor> sensors) {
         boolean hasFace = false;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b0f8ee1..92b6a08 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -5495,7 +5495,8 @@
             final int result = checkPermission(mContext, permission, attributionSource, message,
                     forDataDelivery, startDataDelivery, fromDatasource, attributedOp);
             // Finish any started op if some step in the attribution chain failed.
-            if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
+            if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED
+                    && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
                 if (attributedOp == AppOpsManager.OP_NONE) {
                     finishDataDelivery(AppOpsManager.permissionToOpCode(permission),
                             attributionSource.asState(), fromDatasource);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index e5a634f..b6b8ad1 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -128,12 +128,9 @@
         if (w == null || winHint != null && w != winHint) {
             return;
         }
-        final boolean surfaceReady = w.isDrawn()  // Regular case
-                || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
-        final boolean needsLetterbox = surfaceReady && shouldShowLetterboxUi(w);
         updateRoundedCorners(w);
         updateWallpaperForLetterbox(w);
-        if (needsLetterbox) {
+        if (shouldShowLetterboxUi(w)) {
             if (mLetterbox == null) {
                 mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
                         mActivityRecord.mWmService.mTransactionFactory,
@@ -161,19 +158,26 @@
         }
     }
 
-    /**
-     * @return {@code true} when the main window is letterboxed, this activity isn't transparent
-     * and doesn't show a wallpaper.
-     */
     @VisibleForTesting
     boolean shouldShowLetterboxUi(WindowState mainWindow) {
-        return mainWindow.areAppWindowBoundsLetterboxed() && mActivityRecord.fillsParent()
+        return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
+                // Check that an activity isn't transparent.
+                && mActivityRecord.fillsParent()
                 // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
                 // WindowContainer#showWallpaper because the later will return true when this
                 // activity is using blurred wallpaper for letterbox backgroud.
                 && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
     }
 
+    @VisibleForTesting
+    boolean isSurfaceReadyAndVisible(WindowState mainWindow) {
+        boolean surfaceReady = mainWindow.isDrawn() // Regular case
+                // Waiting for relayoutWindow to call preserveSurface
+                || mainWindow.isDragResizeChanged();
+        return surfaceReady && (mActivityRecord.isVisible()
+                || mActivityRecord.isVisibleRequested());
+    }
+
     private Color getLetterboxBackgroundColor() {
         final WindowState w = mActivityRecord.findMainWindow();
         if (w == null || w.isLetterboxedForDisplayCutout()) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 2b40b75..5362771 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -156,6 +156,7 @@
     @VisibleForTesting
     boolean mShouldAttachNavBarToAppDuringTransition;
     private boolean mNavigationBarAttachedToApp;
+    private ActivityRecord mNavBarAttachedApp;
 
     /**
      * Animates the screenshot of task that used to be controlled by RecentsAnimation.
@@ -392,6 +393,18 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void animateNavigationBarToApp(long duration) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    animateNavigationBarForAppLaunch(duration);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     /**
@@ -613,7 +626,6 @@
                 || mDisplayContent.getFadeRotationAnimationController() != null) {
             return;
         }
-        ActivityRecord topActivity = null;
         boolean shouldTranslateNavBar = false;
         final boolean isDisplayLandscape =
                 mDisplayContent.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
@@ -630,12 +642,12 @@
                 continue;
             }
             shouldTranslateNavBar = isSplitScreenSecondary;
-            topActivity = task.getTopVisibleActivity();
+            mNavBarAttachedApp = task.getTopVisibleActivity();
             break;
         }
 
         final WindowState navWindow = getNavigationBarWindow();
-        if (topActivity == null || navWindow == null || navWindow.mToken == null) {
+        if (mNavBarAttachedApp == null || navWindow == null || navWindow.mToken == null) {
             return;
         }
         mNavigationBarAttachedToApp = true;
@@ -643,9 +655,9 @@
         final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
         final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
         if (shouldTranslateNavBar) {
-            navWindow.setSurfaceTranslationY(-topActivity.getBounds().top);
+            navWindow.setSurfaceTranslationY(-mNavBarAttachedApp.getBounds().top);
         }
-        t.reparent(navSurfaceControl, topActivity.getSurfaceControl());
+        t.reparent(navSurfaceControl, mNavBarAttachedApp.getSurfaceControl());
         t.show(navSurfaceControl);
 
         final WindowContainer imeContainer = mDisplayContent.getImeContainer();
@@ -695,9 +707,25 @@
         }
     }
 
+    void animateNavigationBarForAppLaunch(long duration) {
+        if (!mShouldAttachNavBarToAppDuringTransition
+                // Skip the case where the nav bar is controlled by fade rotation.
+                || mDisplayContent.getFadeRotationAnimationController() != null
+                || mNavigationBarAttachedToApp
+                || mNavBarAttachedApp == null) {
+            return;
+        }
+
+        final NavBarFadeAnimationController controller =
+                new NavBarFadeAnimationController(mDisplayContent);
+        controller.fadeOutAndInSequentially(duration, null /* fadeOutParent */,
+                mNavBarAttachedApp.getSurfaceControl());
+    }
+
     void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
         if (mRunner != null) {
             mIsAddingTaskToTargets = task != null;
+            mNavBarAttachedApp = task == null ? null : task.getTopVisibleActivity();
             // No need to send task appeared when the task target already exists, or when the
             // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
             if (isAnimatingTask(task) || skipAnimation(task)) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b754644..e13ef99 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1987,43 +1987,39 @@
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
     jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor);
 
-    std::vector<int> lightIds = im->getInputManager()->getReader()->getLightIds(deviceId);
+    std::vector<InputDeviceLightInfo> lights =
+            im->getInputManager()->getReader()->getLights(deviceId);
 
-    for (size_t i = 0; i < lightIds.size(); i++) {
-        const InputDeviceLightInfo* lightInfo =
-                im->getInputManager()->getReader()->getLightInfo(deviceId, lightIds[i]);
-        if (lightInfo == nullptr) {
-            ALOGW("Failed to get input device %d light info for id %d", deviceId, lightIds[i]);
-            continue;
-        }
+    for (size_t i = 0; i < lights.size(); i++) {
+        const InputDeviceLightInfo& lightInfo = lights[i];
 
         jint jTypeId =
                 env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
         jint jCapability = 0;
 
-        if (lightInfo->type == InputDeviceLightType::MONO) {
+        if (lightInfo.type == InputDeviceLightType::MONO) {
             jCapability = env->GetStaticIntField(gLightClassInfo.clazz,
                                                  gLightClassInfo.lightCapabilityBrightness);
-        } else if (lightInfo->type == InputDeviceLightType::RGB ||
-                   lightInfo->type == InputDeviceLightType::MULTI_COLOR) {
+        } else if (lightInfo.type == InputDeviceLightType::RGB ||
+                   lightInfo.type == InputDeviceLightType::MULTI_COLOR) {
             jCapability =
                 env->GetStaticIntField(gLightClassInfo.clazz,
                                                  gLightClassInfo.lightCapabilityBrightness) |
                 env->GetStaticIntField(gLightClassInfo.clazz,
                                                  gLightClassInfo.lightCapabilityRgb);
-        } else if (lightInfo->type == InputDeviceLightType::PLAYER_ID) {
+        } else if (lightInfo.type == InputDeviceLightType::PLAYER_ID) {
             jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
                                                  gLightClassInfo.lightTypePlayerId);
         } else {
-            ALOGW("Unknown light type %d", lightInfo->type);
+            ALOGW("Unknown light type %d", lightInfo.type);
             continue;
         }
         ScopedLocalRef<jobject> lightObj(env,
                                          env->NewObject(gLightClassInfo.clazz,
                                                         gLightClassInfo.constructor,
-                                                        static_cast<jint>(lightInfo->id),
-                                                        env->NewStringUTF(lightInfo->name.c_str()),
-                                                        static_cast<jint>(lightInfo->ordinal),
+                                                        static_cast<jint>(lightInfo.id),
+                                                        env->NewStringUTF(lightInfo.name.c_str()),
+                                                        static_cast<jint>(lightInfo.ordinal),
                                                         jTypeId, jCapability));
         // Add light object to list
         env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get());
@@ -2218,39 +2214,28 @@
 
 static jobjectArray nativeGetSensorList(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-    std::vector<InputDeviceInfo> devices = im->getInputManager()->getReader()->getInputDevices();
-    // Find the input device by deviceId
-    auto it = std::find_if(devices.begin(), devices.end(),
-                           [deviceId](InputDeviceInfo& info) { return info.getId() == deviceId; });
+    std::vector<InputDeviceSensorInfo> sensors =
+            im->getInputManager()->getReader()->getSensors(deviceId);
 
-    if (it == devices.end()) {
-        // Return an array of size 0
-        return env->NewObjectArray(0, gInputSensorInfo.clazz, nullptr);
-    }
+    jobjectArray arr = env->NewObjectArray(sensors.size(), gInputSensorInfo.clazz, nullptr);
+    for (int i = 0; i < sensors.size(); i++) {
+        const InputDeviceSensorInfo& sensorInfo = sensors[i];
 
-    std::vector<InputDeviceSensorType> types = it->getSensorTypes();
-    jobjectArray arr = env->NewObjectArray(types.size(), gInputSensorInfo.clazz, nullptr);
-    for (int i = 0; i < types.size(); i++) {
-        const InputDeviceSensorInfo* sensorInfo = it->getSensorInfo(types[i]);
-        if (sensorInfo == nullptr) {
-            ALOGW("Failed to get input device %d sensor info for type %s", deviceId,
-                  NamedEnum::string(types[i]).c_str());
-            continue;
-        }
-
-        jobject info =
-                createInputSensorInfo(env, env->NewStringUTF(sensorInfo->name.c_str()),
-                                      env->NewStringUTF(sensorInfo->vendor.c_str()),
-                                      (jint)sensorInfo->version, 0 /* handle */,
-                                      (jint)sensorInfo->type, (jfloat)sensorInfo->maxRange,
-                                      (jfloat)sensorInfo->resolution, (jfloat)sensorInfo->power,
-                                      (jfloat)sensorInfo->minDelay,
-                                      (jint)sensorInfo->fifoReservedEventCount,
-                                      (jint)sensorInfo->fifoMaxEventCount,
-                                      env->NewStringUTF(sensorInfo->stringType.c_str()),
-                                      env->NewStringUTF("") /* requiredPermission */,
-                                      (jint)sensorInfo->maxDelay, (jint)sensorInfo->flags,
-                                      (jint)sensorInfo->id);
+        jobject info = createInputSensorInfo(env, env->NewStringUTF(sensorInfo.name.c_str()),
+                                             env->NewStringUTF(sensorInfo.vendor.c_str()),
+                                             static_cast<jint>(sensorInfo.version), 0 /* handle */,
+                                             static_cast<jint>(sensorInfo.type),
+                                             static_cast<jfloat>(sensorInfo.maxRange),
+                                             static_cast<jfloat>(sensorInfo.resolution),
+                                             static_cast<jfloat>(sensorInfo.power),
+                                             static_cast<jfloat>(sensorInfo.minDelay),
+                                             static_cast<jint>(sensorInfo.fifoReservedEventCount),
+                                             static_cast<jint>(sensorInfo.fifoMaxEventCount),
+                                             env->NewStringUTF(sensorInfo.stringType.c_str()),
+                                             env->NewStringUTF("") /* requiredPermission */,
+                                             static_cast<jint>(sensorInfo.maxDelay),
+                                             static_cast<jint>(sensorInfo.flags),
+                                             static_cast<jint>(sensorInfo.id));
         env->SetObjectArrayElement(arr, i, info);
         env->DeleteLocalRef(info);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
index 7148ed4..ce9fa2d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/UpdateInstaller.java
@@ -68,7 +68,7 @@
             notifyCallbackOnError(
                     InstallSystemUpdateCallback.UPDATE_ERROR_BATTERY_LOW,
                     "The battery level must be above "
-                            + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or"
+                            + mConstants.BATTERY_THRESHOLD_NOT_CHARGING + " while not charging or "
                             + "above " + mConstants.BATTERY_THRESHOLD_CHARGING + " while charging");
             return;
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 4afb7dd..2892bf5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
 
 import static com.android.server.biometrics.BiometricServiceStateProto.*;
 
@@ -38,7 +39,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustManager;
 import android.content.Context;
-import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.IBiometricAuthenticator;
@@ -67,6 +67,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
+import java.util.function.Consumer;
 
 @Presubmit
 @SmallTest
@@ -245,9 +246,6 @@
             assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
             assertEquals(BiometricSensor.STATE_COOKIE_RETURNED,
                     session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getSensorState());
-            session.onErrorReceived(fingerprintSensorId,
-                    session.mPreAuthInfo.eligibleSensors.get(fingerprintSensorId).getCookie(),
-                    BiometricConstants.BIOMETRIC_ERROR_VENDOR, 0 /* vendorCode */);
             session.onStartFingerprint();
         }
         assertEquals(STATE_AUTH_STARTED_UI_SHOWING, session.getState());
@@ -258,6 +256,21 @@
     @Test
     public void testCancelAuthentication_whenStateAuthCalled_invokesCancel()
             throws RemoteException {
+        testInvokesCancel(session -> session.onCancelAuthSession(false /* force */));
+    }
+
+    @Test
+    public void testCancelAuthentication_whenStateAuthForcedCalled_invokesCancel()
+            throws RemoteException {
+        testInvokesCancel(session -> session.onCancelAuthSession(true /* force */));
+    }
+
+    @Test
+    public void testCancelAuthentication_whenDialogDismissed() throws RemoteException {
+        testInvokesCancel(session -> session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null));
+    }
+
+    private void testInvokesCancel(Consumer<AuthSession> sessionConsumer) throws RemoteException {
         final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class);
 
         setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator);
@@ -269,7 +282,8 @@
 
         session.goToInitialState();
         assertEquals(STATE_AUTH_CALLED, session.getState());
-        session.onCancelAuthSession(false /* force */);
+
+        sessionConsumer.accept(session);
 
         verify(faceAuthenticator).cancelAuthenticationFromService(eq(mToken), eq(TEST_PACKAGE));
     }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index ae836ce..7c7afb7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -1036,7 +1036,7 @@
     }
 
     @Test
-    public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
+    public void testDismissedReasonNegative_whilePaused_invokeHalCancel() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
         invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                 false /* requireConfirmation */, null /* authenticators */);
@@ -1050,14 +1050,12 @@
                 BiometricPrompt.DISMISSED_REASON_NEGATIVE, null /* credentialAttestation */);
         waitForIdle();
 
-        verify(mBiometricService.mSensors.get(0).impl,
-                never()).cancelAuthenticationFromService(
-                any(),
-                any());
+        verify(mBiometricService.mSensors.get(0).impl)
+                .cancelAuthenticationFromService(any(), any());
     }
 
     @Test
-    public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws
+    public void testDismissedReasonUserCancel_whilePaused_invokesHalCancel() throws
             Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
         invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
@@ -1072,10 +1070,8 @@
                 BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
         waitForIdle();
 
-        verify(mBiometricService.mSensors.get(0).impl,
-                never()).cancelAuthenticationFromService(
-                any(),
-                any());
+        verify(mBiometricService.mSensors.get(0).impl)
+                .cancelAuthenticationFromService(any(), any());
     }
 
     @Test
@@ -1091,11 +1087,8 @@
                 BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
         waitForIdle();
 
-        // doesn't send cancel to HAL
-        verify(mBiometricService.mSensors.get(0).impl,
-                never()).cancelAuthenticationFromService(
-                any(),
-                any());
+        verify(mBiometricService.mSensors.get(0).impl)
+                .cancelAuthenticationFromService(any(), any());
         verify(mReceiver1).onError(
                 eq(BiometricAuthenticator.TYPE_FACE),
                 eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
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 db7e437..d2270b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -50,6 +50,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.same;
@@ -357,6 +358,11 @@
         final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window");
 
         assertEquals(window, mActivity.findMainWindow());
+
+        spyOn(mActivity.mLetterboxUiController);
+        doReturn(true).when(mActivity.mLetterboxUiController)
+                .isSurfaceReadyAndVisible(any());
+
         assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
                 mActivity.findMainWindow()));
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 0bb09a9..3182501 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -131,6 +131,17 @@
             protected long getAutoDisconnectTimeoutMs() {
                 return -1;
             }
+
+            @Override
+            public void binderDied() {
+                super.binderDied();
+                Slog.w(TAG, "binderDied");
+                try {
+                    callback.onError(-1);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to report onError status: " + e);
+                }
+            }
         };
         mRemoteHotwordDetectionService.connect();
         if (callback == null) {
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 4d5b6ac..88efe1f 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1069,6 +1069,13 @@
      */
     public static final int NO_DEFAULT_DATA = 0x10008;
 
+    /**
+     * Data service is temporarily unavailable.
+     *
+     * @hide
+     */
+    public static final int SERVICE_TEMPORARILY_UNAVAILABLE = 0x10009;
+
     private static final Map<Integer, String> sFailCauseMap;
     static {
         sFailCauseMap = new HashMap<>();
@@ -1500,6 +1507,7 @@
         sFailCauseMap.put(HANDOVER_FAILED, "HANDOVER_FAILED");
         sFailCauseMap.put(DUPLICATE_CID, "DUPLICATE_CID");
         sFailCauseMap.put(NO_DEFAULT_DATA, "NO_DEFAULT_DATA");
+        sFailCauseMap.put(SERVICE_TEMPORARILY_UNAVAILABLE, "SERVICE_TEMPORARILY_UNAVAILABLE");
     }
 
     private DataFailCause() {
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 363e47a..d082715 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -63,6 +63,11 @@
     public static final int RESULT_ERROR_BUSY           = 3;
     /** Request sent in illegal state */
     public static final int RESULT_ERROR_ILLEGAL_STATE  = 4;
+    /**
+     * Service is temporarily unavailable. Frameworks should retry the request again.
+     * @hide
+     */
+    public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
 
     private final IDataServiceCallback mCallback;