Merge "Fix One-handed mode truncated screen when device rotates in multiple user mode" into sc-v2-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 9eb7bb71..84d05c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2918,6 +2918,13 @@
                             reasonCode, reason).sendToTarget();
                 }
                 reportTempWhitelistChangedLocked(uid, true);
+            } else {
+                // The uid is already temp allowlisted, only need to update AMS for temp allowlist
+                // duration.
+                if (mLocalActivityManager != null) {
+                    mLocalActivityManager.updateDeviceIdleTempAllowlist(null, uid, true,
+                            duration, tempAllowListType, reasonCode, reason, callingUid);
+                }
             }
         }
         if (informWhitelistChanged) {
diff --git a/core/api/current.txt b/core/api/current.txt
index ec8dce0..4df648c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1298,6 +1298,7 @@
     field public static final int shortcutLongLabel = 16844074; // 0x101052a
     field public static final int shortcutShortLabel = 16844073; // 0x1010529
     field public static final int shouldDisableView = 16843246; // 0x10101ee
+    field public static final int shouldUseDefaultUnfoldTransition;
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
@@ -6926,6 +6927,7 @@
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
+    method public boolean shouldUseDefaultUnfoldTransition();
     method public boolean supportsMultipleDisplays();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -35273,7 +35275,6 @@
     field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
     field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
     field public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI = "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_URI";
-    field public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT = "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT";
     field public static final String EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY = "android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
     field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
     field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
@@ -46890,8 +46891,15 @@
   }
 
   @UiThread public interface AttachedSurfaceControl {
+    method public default void addOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener);
     method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
     method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
+    method public default int getSurfaceTransformHint();
+    method public default void removeOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener);
+  }
+
+  @UiThread public static interface AttachedSurfaceControl.OnSurfaceTransformHintChangedListener {
+    method public void onSurfaceTransformHintChanged(int);
   }
 
   public final class Choreographer {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9c01bca..1d406a5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5379,12 +5379,14 @@
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnHeadToSoundstagePoseUpdatedListener();
     method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getDesiredHeadTrackingMode();
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void getEffectParameter(int, @NonNull byte[]);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
     method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeOnHeadTrackingModeChangedListener(@NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setDesiredHeadTrackingMode(int);
+    method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEffectParameter(int, @NonNull byte[]);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
     method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
@@ -14448,6 +14450,7 @@
     method public int getFlags();
     method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId();
     method public int getTaskId();
+    method @Nullable public android.os.IBinder getWindowToken();
     field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
     field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
     field public static final int FLAG_RECONNECTED = 4; // 0x4
@@ -14455,6 +14458,7 @@
 
   public final class ContentCaptureEvent implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.graphics.Rect getBounds();
     method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext();
     method public long getEventTime();
     method @Nullable public android.view.autofill.AutofillId getId();
@@ -14474,6 +14478,7 @@
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
     field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5
     field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4
+    field public static final int TYPE_WINDOW_BOUNDS_CHANGED = 10; // 0xa
   }
 
   public final class ContentCaptureManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4376d22..f53c5b6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -771,6 +771,11 @@
         return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
     }
 
+    /** @hide Should this process state be considered in the cache? */
+    public static final boolean isProcStateCached(int procState) {
+        return procState >= PROCESS_STATE_CACHED_ACTIVITY;
+    }
+
     /** @hide Is this a foreground service type? */
     public static boolean isForegroundService(int procState) {
         return procState == PROCESS_STATE_FOREGROUND_SERVICE;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d5f51ad..70bb132 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -156,6 +156,7 @@
     /**
      * Update information about which app IDs are on the temp allowlist.
      * @param appids the updated list of appIds in temp allowlist.
+     *               If null, it is to update only changingUid.
      * @param changingUid uid to add or remove to temp allowlist.
      * @param adding true to add to temp allowlist, false to remove from temp allowlist.
      * @param durationMs when adding is true, the duration to be in temp allowlist.
@@ -165,7 +166,7 @@
      * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding
      *                   is true.
      */
-    public abstract void updateDeviceIdleTempAllowlist(int[] appids, int changingUid,
+    public abstract void updateDeviceIdleTempAllowlist(@Nullable int[] appids, int changingUid,
             boolean adding, long durationMs, @TempAllowListType int type,
             @ReasonCode int reasonCode,
             @Nullable String reason, int callingUid);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e22b92..80554d7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3616,6 +3616,11 @@
                 }
                 activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
                 activity.mCalled = false;
+                // Assigning the activity to the record before calling onCreate() allows
+                // ActivityThread#getActivity() lookup for the callbacks triggered from
+                // ActivityLifecycleCallbacks#onActivityCreated() or
+                // ActivityLifecycleCallback#onActivityPostCreated().
+                r.activity = activity;
                 if (r.isPersistable()) {
                     mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                 } else {
@@ -3626,7 +3631,6 @@
                         "Activity " + r.intent.getComponent().toShortString() +
                         " did not call through to super.onCreate()");
                 }
-                r.activity = activity;
                 mLastReportedWindowingMode.put(activity.getActivityToken(),
                         config.windowConfiguration.getWindowingMode());
             }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 719025f..61b1abe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6506,25 +6506,34 @@
 
             if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
                     && !styleDisplaysCustomViewInline()) {
-                if (mN.contentView == null) {
-                    mN.contentView = createContentView();
+                RemoteViews newContentView = mN.contentView;
+                RemoteViews newBigContentView = mN.bigContentView;
+                RemoteViews newHeadsUpContentView = mN.headsUpContentView;
+                if (newContentView == null) {
+                    newContentView = createContentView();
                     mN.extras.putInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
-                            mN.contentView.getSequenceNumber());
+                            newContentView.getSequenceNumber());
                 }
-                if (mN.bigContentView == null) {
-                    mN.bigContentView = createBigContentView();
-                    if (mN.bigContentView != null) {
+                if (newBigContentView == null) {
+                    newBigContentView = createBigContentView();
+                    if (newBigContentView != null) {
                         mN.extras.putInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
-                                mN.bigContentView.getSequenceNumber());
+                                newBigContentView.getSequenceNumber());
                     }
                 }
-                if (mN.headsUpContentView == null) {
-                    mN.headsUpContentView = createHeadsUpContentView();
-                    if (mN.headsUpContentView != null) {
+                if (newHeadsUpContentView == null) {
+                    newHeadsUpContentView = createHeadsUpContentView();
+                    if (newHeadsUpContentView != null) {
                         mN.extras.putInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
-                                mN.headsUpContentView.getSequenceNumber());
+                                newHeadsUpContentView.getSequenceNumber());
                     }
                 }
+                // Don't set any of the content views until after they have all been generated,
+                //  to avoid the generated .contentView triggering the logic which skips generating
+                //  the .bigContentView.
+                mN.contentView = newContentView;
+                mN.bigContentView = newBigContentView;
+                mN.headsUpContentView = newHeadsUpContentView;
             }
 
             if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index e9b0175..4dff4e0 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -81,6 +81,7 @@
     final int mContextDescriptionResource;
     final boolean mShowMetadataInPreview;
     final boolean mSupportsAmbientMode;
+    final boolean mShouldUseDefaultUnfoldTransition;
     final String mSettingsSliceUri;
     final boolean mSupportMultipleDisplays;
 
@@ -145,6 +146,9 @@
             mSupportsAmbientMode = sa.getBoolean(
                     com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
                     false);
+            mShouldUseDefaultUnfoldTransition = sa.getBoolean(
+                    com.android.internal.R.styleable.Wallpaper_shouldUseDefaultUnfoldTransition,
+                    true);
             mSettingsSliceUri = sa.getString(
                     com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
             mSupportMultipleDisplays = sa.getBoolean(
@@ -171,6 +175,7 @@
         mSupportsAmbientMode = source.readInt() != 0;
         mSettingsSliceUri = source.readString();
         mSupportMultipleDisplays = source.readInt() != 0;
+        mShouldUseDefaultUnfoldTransition = source.readInt() != 0;
         mService = ResolveInfo.CREATOR.createFromParcel(source);
     }
     
@@ -393,6 +398,26 @@
         return mSupportMultipleDisplays;
     }
 
+    /**
+     * Returns whether this wallpaper should receive default zooming updates when unfolding.
+     * If set to false the wallpaper will not receive zoom events when folding or unfolding
+     * a foldable device, so it can implement its own unfold transition.
+     * <p>
+     * This corresponds to the value {@link
+     * android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition} in the XML description
+     * of the wallpaper.
+     * <p>
+     * The default value is {@code true}.
+     *
+     * @see android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
+     * @return {@code true} if wallpaper should receive default fold/unfold transition updates
+     *
+     * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
+     */
+    public boolean shouldUseDefaultUnfoldTransition() {
+        return mShouldUseDefaultUnfoldTransition;
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Service:");
         mService.dump(pw, prefix + "  ");
@@ -423,6 +448,7 @@
         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
         dest.writeString(mSettingsSliceUri);
         dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
+        dest.writeInt(mShouldUseDefaultUnfoldTransition ? 1 : 0);
         mService.writeToParcel(dest, flags);
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 08e95a2..c3ec094 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5935,6 +5935,10 @@
      * more general access to the URI's content provider then this check will
      * always fail.
      *
+     * <strong>Note:</strong> On SDK Version {@link android.os.Build.VERSION_CODES#S},
+     * calling this method from a secondary-user's context will incorrectly return
+     * {@link PackageManager#PERMISSION_DENIED} for all {code uris}.
+     *
      * @param uris The list of URIs that is being checked.
      * @param pid The process ID being checked against.  Must be &gt; 0.
      * @param uid The user ID being checked against.  A uid of 0 is the root
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32..35794d7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2738,6 +2738,22 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+    /**
+     * Broadcast Action: One of the suspend conditions have been modified for the packages.
+     * <p>Includes the following extras:
+     * <ul>
+     * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been modified
+     * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been modified
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. It is only sent to registered receivers.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGES_SUSPENSION_CHANGED =
+            "android.intent.action.PACKAGES_SUSPENSION_CHANGED";
 
     /**
      * Broadcast Action: Distracting packages have been changed.
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 5cfba3d..395c655 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -263,12 +263,12 @@
                     @Override
                     public void onServiceConnected(ComponentName component, IBinder binder) {
                         mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
-                        mInitFuture.setStatus(true);
                         try {
                             mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
                         } catch (RemoteException e) {
                             Log.e(TAG, "Remote IPC failed!");
                         }
+                        mInitFuture.setStatus(true);
                     }
                 };
                 ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT |
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 3b1cb94..425f22c 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -58,7 +58,7 @@
 
     private static final class JpegParameters {
         public HashSet<Long> mTimeStamps = new HashSet<>();
-        public int mRotation = JPEG_DEFAULT_ROTATION; // CCW multiple of 90 degrees
+        public int mRotation = JPEG_DEFAULT_ROTATION; // CW multiple of 90 degrees
         public int mQuality = JPEG_DEFAULT_QUALITY; // [0..100]
     }
 
@@ -100,7 +100,8 @@
             Integer orientation = captureBundles.get(0).captureResult.get(
                     CaptureResult.JPEG_ORIENTATION);
             if (orientation != null) {
-                ret.mRotation = orientation / 90;
+                // The jpeg encoder expects CCW rotation, convert from CW
+                ret.mRotation = (360 - (orientation % 360)) / 90;
             } else {
                 Log.w(TAG, "No jpeg rotation set, using default: " + JPEG_DEFAULT_ROTATION);
             }
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java b/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
new file mode 100644
index 0000000..4c91c16
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+/**
+ * Device state manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class DeviceStateManagerInternal {
+
+    /** Returns the list of currently supported device state identifiers. */
+    public abstract int[] getSupportedStateIdentifiers();
+}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4f20553..5bb51c1 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -35,6 +35,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Display manager local system service interface.
@@ -129,6 +130,14 @@
     public abstract DisplayInfo getDisplayInfo(int displayId);
 
     /**
+     * Returns a set of DisplayInfo, for the states that may be assumed by either the given display,
+     * or any other display within that display's group.
+     *
+     * @param displayId The logical display id to fetch DisplayInfo for.
+     */
+    public abstract Set<DisplayInfo> getPossibleDisplayInfo(int displayId);
+
+    /**
      * Returns the position of the display's projection.
      *
      * @param displayId The logical display id.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d48d562..a3d595c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -146,6 +146,7 @@
     private CryptoObject mCryptoObject;
     @Nullable private RemoveTracker mRemoveTracker;
     private Handler mHandler;
+    @Nullable private float[] mEnrollStageThresholds;
 
     /**
      * Retrieves a list of properties for all fingerprint sensors on the device.
@@ -1329,6 +1330,46 @@
     /**
      * @hide
      */
+    public int getEnrollStageCount() {
+        if (mEnrollStageThresholds == null) {
+            mEnrollStageThresholds = createEnrollStageThresholds(mContext);
+        }
+        return mEnrollStageThresholds.length + 1;
+    }
+
+    /**
+     * @hide
+     */
+    public float getEnrollStageThreshold(int index) {
+        if (mEnrollStageThresholds == null) {
+            mEnrollStageThresholds = createEnrollStageThresholds(mContext);
+        }
+
+        if (index < 0 || index > mEnrollStageThresholds.length) {
+            Slog.w(TAG, "Unsupported enroll stage index: " + index);
+            return index < 0 ? 0f : 1f;
+        }
+
+        // The implicit threshold for the final stage is always 1.
+        return index == mEnrollStageThresholds.length ? 1f : mEnrollStageThresholds[index];
+    }
+
+    @NonNull
+    private static float[] createEnrollStageThresholds(@NonNull Context context) {
+        // TODO(b/200604947): Fetch this value from FingerprintService, rather than internal config
+        final String[] enrollStageThresholdStrings = context.getResources().getStringArray(
+                com.android.internal.R.array.config_udfps_enroll_stage_thresholds);
+
+        final float[] enrollStageThresholds = new float[enrollStageThresholdStrings.length];
+        for (int i = 0; i < enrollStageThresholds.length; i++) {
+            enrollStageThresholds[i] = Float.parseFloat(enrollStageThresholdStrings[i]);
+        }
+        return enrollStageThresholds;
+    }
+
+    /**
+     * @hide
+     */
     public static String getErrorString(Context context, int errMsg, int vendorCode) {
         switch (errMsg) {
             case FINGERPRINT_ERROR_HW_UNAVAILABLE:
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f483752..0f94cbe 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -271,6 +271,16 @@
     }
 
     /**
+     * Returns the names of custom power components in order, so the first name in the array
+     * corresponds to the custom componentId
+     * {@link BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID}.
+     */
+    @NonNull
+    public String[] getCustomPowerComponentNames() {
+        return mCustomPowerComponentNames;
+    }
+
+    /**
      * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
      */
     @NonNull
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 3d466a0..c646623 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -74,7 +74,7 @@
         private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
         private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
         // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
-        private static final int CRASH_AT_SIZE = 20_000;
+        private static final int CRASH_AT_SIZE = 25_000;
 
         /**
          * We next warn when we exceed this bucket size.
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3aa0bcb..4dae7c7 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -548,6 +548,7 @@
             WAKE_REASON_HDMI,
             WAKE_REASON_DISPLAY_GROUP_ADDED,
             WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
+            WAKE_REASON_UNFOLD_DEVICE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WakeReason{}
@@ -647,6 +648,12 @@
     public static final int WAKE_REASON_DISPLAY_GROUP_TURNED_ON = 11;
 
     /**
+     * Wake up reason code: Waking the device due to unfolding of a foldable device.
+     * @hide
+     */
+    public static final int WAKE_REASON_UNFOLD_DEVICE = 12;
+
+    /**
      * Convert the wake reason to a string for debugging purposes.
      * @hide
      */
@@ -664,6 +671,7 @@
             case WAKE_REASON_LID: return "WAKE_REASON_LID";
             case WAKE_REASON_DISPLAY_GROUP_ADDED: return "WAKE_REASON_DISPLAY_GROUP_ADDED";
             case WAKE_REASON_DISPLAY_GROUP_TURNED_ON: return "WAKE_REASON_DISPLAY_GROUP_TURNED_ON";
+            case WAKE_REASON_UNFOLD_DEVICE: return "WAKE_REASON_UNFOLD_DEVICE";
             default: return Integer.toString(wakeReason);
         }
     }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 54905ec..8928a42 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.IVold;
 
 import java.util.List;
@@ -135,4 +136,19 @@
      * {@link VolumeInfo#isPrimary()}
      */
     public abstract List<String> getPrimaryVolumeIds();
+
+    /**
+     * Tells StorageManager that CE storage for this user has been prepared.
+     *
+     * @param userId userId for which CE storage has been prepared
+     */
+    public abstract void markCeStoragePrepared(@UserIdInt int userId);
+
+    /**
+     * Returns true when CE storage for this user has been prepared.
+     *
+     * When the user key is unlocked and CE storage has been prepared,
+     * it's ok to access and modify CE directories on volumes for this user.
+     */
+    public abstract boolean isCeStoragePrepared(@UserIdInt int userId);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0899ef5..7472cca 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -35,7 +35,6 @@
 import android.app.AutomaticZenRule;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -2244,6 +2243,21 @@
     public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
 
     /**
+     * Activity Action: Show screen that lets user configure wifi tethering.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+     * <p>
+     * Input: Nothing
+     * <p>
+     * Output: Nothing
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_WIFI_TETHER_SETTING =
+            "com.android.settings.WIFI_TETHER_SETTINGS";
+
+    /**
      * Broadcast to trigger notification of asking user to enable MMS.
      * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
      *
@@ -8966,6 +8980,15 @@
         public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms";
 
         /**
+         * Indicates whether the spatial audio feature was enabled for this user.
+         *
+         * Type : int (0 disabled, 1 enabled)
+         *
+         * @hide
+         */
+        public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled";
+
+        /**
          * Indicates whether notification display on the lock screen is enabled.
          * <p>
          * Type: int (0 for false, 1 for true)
@@ -16952,9 +16975,6 @@
      *
      *     Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY} must be included to
      * specify a key that indicates the menu item which will be highlighted on settings home menu.
-     *
-     *     Input: {@link #EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT} is optional. Apps
-     * can use the {@link PendingIntent} extra to launch into its private {@link Activity}.
      * <p>
      * Output: Nothing.
      */
@@ -16982,15 +17002,6 @@
             "android.provider.extra.SETTINGS_LARGE_SCREEN_HIGHLIGHT_MENU_KEY";
 
     /**
-     * Activity Extra: Apps can use the {@link PendingIntent} extra to launch into its private
-     * {@link Activity}.
-     * <p>
-     * This is an optional extra field to {@link #ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK}.
-     */
-    public static final String EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT =
-            "android.provider.extra.SETTINGS_LARGE_SCREEN_DEEP_LINK_PENDING_INTENT";
-
-    /**
      * Performs a strict and comprehensive check of whether a calling package is allowed to
      * write/modify system settings, as the condition differs for pre-M, M+, and
      * privileged/preinstalled apps. If the provided uid does not match the
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index cb5e51c..2d263a5 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -561,12 +561,16 @@
          */
         public void reportEngineShown(boolean waitForEngineShown) {
             if (mIWallpaperEngine.mShownReported) return;
-            Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
             if (!waitForEngineShown) {
+                Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
                 mCaller.removeMessages(MSG_REPORT_SHOWN);
                 mCaller.sendMessage(message);
             } else {
-                mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(1));
+                // if we are already waiting, no need to reset the timeout.
+                if (!mCaller.hasMessages(MSG_REPORT_SHOWN)) {
+                    Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
+                    mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5));
+                }
             }
         }
 
@@ -2256,6 +2260,8 @@
                 mShownReported = true;
                 try {
                     mConnection.engineShown(this);
+                    Log.d(TAG, "Wallpaper has updated the surface:"
+                            + mWallpaperManager.getWallpaperInfo());
                 } catch (RemoteException e) {
                     Log.w(TAG, "Wallpaper host disappeared", e);
                     return;
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 362ea8c..5e647a4 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -115,7 +115,7 @@
             @NonNull AttributionSource attributionSource) {
         try {
             if (mCurrentCallback == null) {
-                boolean preflightPermissionCheckPassed = checkPermissionForPreflight(
+                boolean preflightPermissionCheckPassed = checkPermissionForPreflightNotHardDenied(
                         attributionSource);
                 if (preflightPermissionCheckPassed) {
                     if (DBG) {
@@ -470,10 +470,11 @@
         return mStartedDataDelivery;
     }
 
-    private boolean checkPermissionForPreflight(AttributionSource attributionSource) {
-        return PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
-                Manifest.permission.RECORD_AUDIO, attributionSource)
-                == PermissionChecker.PERMISSION_GRANTED;
+    private boolean checkPermissionForPreflightNotHardDenied(AttributionSource attributionSource) {
+        int result = PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
+                Manifest.permission.RECORD_AUDIO, attributionSource);
+        return result == PermissionChecker.PERMISSION_GRANTED
+                || result == PermissionChecker.PERMISSION_SOFT_DENIED;
     }
 
     void finishDataDelivery() {
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 80387aa..43d186e 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -62,6 +62,13 @@
         return mOriginalTranslationMethod;
     }
 
+    /**
+     * Returns the {@link TextView}'s {@link ViewTranslationResponse}.
+     */
+    public ViewTranslationResponse getViewTranslationResponse() {
+        return mTranslationResponse;
+    }
+
     @Override
     public CharSequence getTransformation(CharSequence source, View view) {
         if (!mAllowLengthChanges) {
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index bcc5b56..b2fc9a0 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -53,4 +53,72 @@
      * to the View hierarchy you may need to call {@link android.view.View#invalidate}
      */
     boolean applyTransactionOnDraw(@NonNull SurfaceControl.Transaction t);
+
+    /**
+     * The transform hint can be used by a buffer producer to pre-rotate the rendering such that the
+     * final transformation in the system composer is identity. This can be very useful when used in
+     * conjunction with the h/w composer HAL in situations where it cannot handle rotations or
+     * handle them with an additional power cost.
+     *
+     * The transform hint should be used with ASurfaceControl APIs when submitting buffers.
+     * Example usage:
+     *
+     * 1. After a configuration change, before dequeuing a buffer, the buffer producer queries the
+     *    function for the transform hint.
+     *
+     * 2. The desired buffer width and height is rotated by the transform hint.
+     *
+     * 3. The producer dequeues a buffer of the new pre-rotated size.
+     *
+     * 4. The producer renders to the buffer such that the image is already transformed, that is
+     *    applying the transform hint to the rendering.
+     *
+     * 5. The producer applies the inverse transform hint to the buffer it just rendered.
+     *
+     * 6. The producer queues the pre-transformed buffer with the buffer transform.
+     *
+     * 7. The composer combines the buffer transform with the display transform.  If the buffer
+     *    transform happens to cancel out the display transform then no rotation is needed and there
+     *    will be no performance penalties.
+     *
+     * Note, when using ANativeWindow APIs in conjunction with a NativeActivity Surface or
+     * SurfaceView Surface, the buffer producer will already have access to the transform hint and
+     * no additional work is needed.
+     */
+    default @Surface.Rotation int getSurfaceTransformHint() {
+        return Surface.ROTATION_0;
+    }
+
+    /**
+     * Surface transform hint change listener.
+     * @see #getSurfaceTransformHint
+     */
+    @UiThread
+    interface OnSurfaceTransformHintChangedListener {
+        /**
+         * @param hint new surface transform hint
+         * @see #getSurfaceTransformHint
+         */
+        void onSurfaceTransformHintChanged(@Surface.Rotation int hint);
+    }
+
+    /**
+     * Registers a surface transform hint changed listener to receive notifications about when
+     * the transform hint changes.
+     *
+     * @see #getSurfaceTransformHint
+     * @see #removeOnSurfaceTransformHintChangedListener
+     */
+    default void addOnSurfaceTransformHintChangedListener(
+            @NonNull OnSurfaceTransformHintChangedListener listener) {
+    }
+
+    /**
+     * Unregisters a surface transform hint changed listener.
+     *
+     * @see #addOnSurfaceTransformHintChangedListener
+     */
+    default void removeOnSurfaceTransformHintChangedListener(
+            @NonNull OnSurfaceTransformHintChangedListener listener) {
+    }
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a6abed0..9da5088 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -175,6 +175,11 @@
             float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data);
 
     /**
+     * Drops the content of the current drag operation for accessibility
+     */
+    boolean dropForAccessibility(IWindow window, int x, int y);
+
+    /**
      * Report the result of a drop action targeted to the given window.
      * consumed is 'true' when the drop was accepted by a valid recipient,
      * 'false' otherwise.
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 3f71d4d..046232a 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -208,6 +208,15 @@
      */
     public final @WindowManager.LayoutParams.WindowType int windowType;
 
+    /**
+     * {@code true} if its parent is also a {@link RemoteAnimationTarget} in the same transition.
+     *
+     * For example, when a TaskFragment is resizing while one of its children is open/close, both
+     * windows will be animation targets. This value will be {@code true} for the child, so that
+     * the handler can choose to handle it differently.
+     */
+    public boolean hasAnimatingParent;
+
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
             Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
             Rect localBounds, Rect screenSpaceBounds,
@@ -265,6 +274,7 @@
         taskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
         allowEnterPip = in.readBoolean();
         windowType = in.readInt();
+        hasAnimatingParent = in.readBoolean();
     }
 
     @Override
@@ -292,6 +302,7 @@
         dest.writeTypedObject(taskInfo, 0 /* flags */);
         dest.writeBoolean(allowEnterPip);
         dest.writeInt(windowType);
+        dest.writeBoolean(hasAnimatingParent);
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -311,6 +322,7 @@
         pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
         pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
+        pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d6186d7..5b8dc40 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -41,6 +41,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.gui.DropInputMode;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DeviceProductInfo;
 import android.hardware.display.DisplayedContentSample;
@@ -150,13 +151,15 @@
             float childRelativeTop, float childRelativeRight, float childRelativeBottom);
     private static native void nativeSetTrustedOverlay(long transactionObj, long nativeObject,
             boolean isTrustedOverlay);
-
+    private static native void nativeSetDropInputMode(
+            long transactionObj, long nativeObject, int flags);
     private static native boolean nativeClearContentFrameStats(long nativeObject);
     private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
     private static native boolean nativeClearAnimationFrameStats();
     private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats);
 
     private static native long[] nativeGetPhysicalDisplayIds();
+    private static native long nativeGetPrimaryPhysicalDisplayId();
     private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
     private static native IBinder nativeCreateDisplay(String name, boolean secure);
     private static native void nativeDestroyDisplay(IBinder displayToken);
@@ -2277,6 +2280,15 @@
     }
 
     /**
+     * Exposed to identify the correct display to apply the primary display orientation. Avoid using
+     * for any other purpose.
+     * @hide
+     */
+    public static long getPrimaryPhysicalDisplayId() {
+        return nativeGetPrimaryPhysicalDisplayId();
+    }
+
+    /**
      * @hide
      */
     public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
@@ -3448,7 +3460,18 @@
             return this;
         }
 
-         /**
+        /**
+         * Sets the input event drop mode on this SurfaceControl and its children. The caller must
+         * hold the ACCESS_SURFACE_FLINGER permission. See {@code InputEventDropMode}.
+         * @hide
+         */
+        public Transaction setDropInputMode(SurfaceControl sc, @DropInputMode int mode) {
+            checkPreconditions(sc);
+            nativeSetDropInputMode(mNativeObject, sc.mNativeObject, mode);
+            return this;
+        }
+
+        /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          *
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 51910e0..60bb99d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1464,19 +1464,18 @@
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
-            if (mSurfaceControl == null) {
-                return;
-            }
-
-            // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
-            // its 2nd frame if RenderThread is running slowly could potentially see
-            // this as false, enter the branch, get pre-empted, then this comes along
-            // and reports a new position, then the UI thread resumes and reports
-            // its position. This could therefore be de-sync'd in that interval, but
-            // the synchronization would violate the rule that RT must never block
-            // on the UI thread which would open up potential deadlocks. The risk of
-            // a single-frame desync is therefore preferable for now.
             synchronized(mSurfaceControlLock) {
+                if (mSurfaceControl == null) {
+                    return;
+                }
+                // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+                // its 2nd frame if RenderThread is running slowly could potentially see
+                // this as false, enter the branch, get pre-empted, then this comes along
+                // and reports a new position, then the UI thread resumes and reports
+                // its position. This could therefore be de-sync'd in that interval, but
+                // the synchronization would violate the rule that RT must never block
+                // on the UI thread which would open up potential deadlocks. The risk of
+                // a single-frame desync is therefore preferable for now.
                 mRtHandlingPositionUpdates = true;
             }
             if (mRTLastReportedPosition.left == left
@@ -1506,8 +1505,11 @@
                 if (mViewVisibility) {
                     mPositionChangedTransaction.show(mSurfaceControl);
                 }
-                applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
-                        getViewRootImpl().mSurface, frameNumber);
+                final ViewRootImpl viewRoot = getViewRootImpl();
+                if (viewRoot != null) {
+                    applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
+                            viewRoot.mSurface, frameNumber);
+                }
                 applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
                 mPendingTransaction = false;
             } catch (Exception ex) {
@@ -1528,7 +1530,7 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            if (DEBUG) {
+            if (DEBUG_POSITION) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
             }
@@ -1540,15 +1542,15 @@
                 mPositionChangedTransaction.clear();
                 mPendingTransaction = false;
             }
-            if (mSurfaceControl == null) {
-                return;
-            }
 
             /**
              * positionLost can be called while UI thread is un-paused so we
              * need to hold the lock here.
              */
             synchronized (mSurfaceControlLock) {
+                if (mSurfaceControl == null) {
+                    return;
+                }
                 mRtTransaction.hide(mSurfaceControl);
                 if (mRtReleaseSurfaces) {
                     mRtReleaseSurfaces = false;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1ecdb90..afdb755 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3515,6 +3515,7 @@
      *                    1             PFLAG4_ALLOW_CLICK_WHEN_DISABLED
      *                   1              PFLAG4_DETACHED
      *                  1               PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE
+     *                 1                PFLAG4_DRAG_A11Y_STARTED
      * |-------|-------|-------|-------|
      */
 
@@ -3586,6 +3587,11 @@
      */
     private static final int PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE = 0x000004000;
 
+    /**
+     * Indicates that the view has started a drag with {@link AccessibilityAction#ACTION_DRAG_START}
+     */
+    private static final int PFLAG4_DRAG_A11Y_STARTED = 0x000008000;
+
     /* End of masks for mPrivateFlags4 */
 
     /** @hide */
@@ -10384,8 +10390,17 @@
         if (mTouchDelegate != null) {
             info.setTouchDelegateInfo(mTouchDelegate.getTouchDelegateInfo());
         }
+
+        if (startedSystemDragForAccessibility()) {
+            info.addAction(AccessibilityAction.ACTION_DRAG_CANCEL);
+        }
+
+        if (canAcceptAccessibilityDrop()) {
+            info.addAction(AccessibilityAction.ACTION_DRAG_DROP);
+        }
     }
 
+
     /**
      * Adds extra data to an {@link AccessibilityNodeInfo} based on an explicit request for the
      * additional data.
@@ -14222,9 +14237,45 @@
                 return true;
             }
         }
+
+        if (action == R.id.accessibilityActionDragDrop) {
+            if (!canAcceptAccessibilityDrop()) {
+                return false;
+            }
+            try {
+                if (mAttachInfo != null && mAttachInfo.mSession != null) {
+                    final int[] location = new int[2];
+                    getLocationInWindow(location);
+                    final int centerX = location[0] + getWidth() / 2;
+                    final int centerY = location[1] + getHeight() / 2;
+                    return mAttachInfo.mSession.dropForAccessibility(mAttachInfo.mWindow,
+                            centerX, centerY);
+                }
+            } catch (RemoteException e) {
+                Log.e(VIEW_LOG_TAG, "Unable to drop for accessibility", e);
+            }
+            return false;
+        } else if (action == R.id.accessibilityActionDragCancel) {
+            if (!startedSystemDragForAccessibility()) {
+                return false;
+            }
+            if (mAttachInfo != null && mAttachInfo.mDragToken != null) {
+                cancelDragAndDrop();
+                return true;
+            }
+            return false;
+        }
         return false;
     }
 
+    private boolean canAcceptAccessibilityDrop() {
+        if (!canAcceptDrag()) {
+            return false;
+        }
+        ListenerInfo li = mListenerInfo;
+        return (li != null) && (li.mOnDragListener != null || li.mOnReceiveContentListener != null);
+    }
+
     private boolean traverseAtGranularity(int granularity, boolean forward,
             boolean extendSelection) {
         CharSequence text = getIterableTextForAccessibility();
@@ -26664,6 +26715,37 @@
             data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
         }
 
+        Rect bounds = new Rect();
+        getBoundsOnScreen(bounds, true);
+
+        Point lastTouchPoint = new Point();
+        mAttachInfo.mViewRootImpl.getLastTouchPoint(lastTouchPoint);
+        final ViewRootImpl root = mAttachInfo.mViewRootImpl;
+
+        // Skip surface logic since shadows and animation are not required during the a11y drag
+        final boolean a11yEnabled = AccessibilityManager.getInstance(mContext).isEnabled();
+        if (a11yEnabled && (flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+            try {
+                IBinder token = mAttachInfo.mSession.performDrag(
+                        mAttachInfo.mWindow, flags, null,
+                        mAttachInfo.mViewRootImpl.getLastTouchSource(),
+                        0f, 0f, 0f, 0f, data);
+                if (ViewDebug.DEBUG_DRAG) {
+                    Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token);
+                }
+                if (token != null) {
+                    root.setLocalDragState(myLocalState);
+                    mAttachInfo.mDragToken = token;
+                    mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+                    setAccessibilityDragStarted(true);
+                }
+                return token != null;
+            } catch (Exception e) {
+                Log.e(VIEW_LOG_TAG, "Unable to initiate a11y drag", e);
+                return false;
+            }
+        }
+
         Point shadowSize = new Point();
         Point shadowTouchPoint = new Point();
         shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
@@ -26688,7 +26770,6 @@
                     + " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
         }
 
-        final ViewRootImpl root = mAttachInfo.mViewRootImpl;
         final SurfaceSession session = new SurfaceSession();
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
@@ -26711,12 +26792,9 @@
                 surface.unlockCanvasAndPost(canvas);
             }
 
-            // repurpose 'shadowSize' for the last touch point
-            root.getLastTouchPoint(shadowSize);
-
-            token = mAttachInfo.mSession.performDrag(
-                    mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(),
-                    shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data);
+            token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
+                    root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y,
+                    shadowTouchPoint.x, shadowTouchPoint.y, data);
             if (ViewDebug.DEBUG_DRAG) {
                 Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
             }
@@ -26728,6 +26806,10 @@
                 mAttachInfo.mDragToken = token;
                 // Cache the local state object for delivery with DragEvents
                 root.setLocalDragState(myLocalState);
+                if (a11yEnabled) {
+                    // Set for AccessibilityEvents
+                    mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+                }
             }
             return token != null;
         } catch (Exception e) {
@@ -26741,6 +26823,24 @@
         }
     }
 
+    void setAccessibilityDragStarted(boolean started) {
+        int pflags4 = mPrivateFlags4;
+        if (started) {
+            pflags4 |= PFLAG4_DRAG_A11Y_STARTED;
+        } else {
+            pflags4 &= ~PFLAG4_DRAG_A11Y_STARTED;
+        }
+
+        if (pflags4 != mPrivateFlags4) {
+            mPrivateFlags4 = pflags4;
+            sendWindowContentChangedAccessibilityEvent(CONTENT_CHANGE_TYPE_UNDEFINED);
+        }
+    }
+
+    private boolean startedSystemDragForAccessibility() {
+        return (mPrivateFlags4 & PFLAG4_DRAG_A11Y_STARTED) != 0;
+    }
+
     /**
      * Cancels an ongoing drag and drop operation.
      * <p>
@@ -26958,6 +27058,12 @@
         }
 
         switch (event.mAction) {
+            case DragEvent.ACTION_DRAG_STARTED: {
+                if (result && li.mOnDragListener != null) {
+                    sendWindowContentChangedAccessibilityEvent(
+                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                }
+            } break;
             case DragEvent.ACTION_DRAG_ENTERED: {
                 mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
                 refreshDrawableState();
@@ -26966,7 +27072,15 @@
                 mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
                 refreshDrawableState();
             } break;
+            case DragEvent.ACTION_DROP: {
+                if (result && (li.mOnDragListener != null | li.mOnReceiveContentListener != null)) {
+                    sendWindowContentChangedAccessibilityEvent(
+                            AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_DROPPED);
+                }
+            } break;
             case DragEvent.ACTION_DRAG_ENDED: {
+                sendWindowContentChangedAccessibilityEvent(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                 mPrivateFlags2 &= ~View.DRAG_MASK;
                 refreshDrawableState();
             } break;
@@ -26979,6 +27093,15 @@
         return (mPrivateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0;
     }
 
+    void sendWindowContentChangedAccessibilityEvent(int changeType) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+            event.setContentChangeTypes(changeType);
+            sendAccessibilityEventUnchecked(event);
+        }
+    }
+
     /**
      * This needs to be a better API (NOT ON VIEW) before it is exposed.  If
      * it is ever exposed at all.
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 775c15e..49f5229 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -412,6 +412,9 @@
      *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
      *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
      *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED}
+     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED}
+     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED}
      *            </ul>
      */
     public void notifySubtreeAccessibilityStateChanged(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4c36fc9..20888f7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -312,6 +312,9 @@
     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>();
     static boolean sFirstDrawComplete = false;
 
+    private ArrayList<OnSurfaceTransformHintChangedListener> mTransformHintListeners =
+            new ArrayList<>();
+    private @Surface.Rotation int mPreviousTransformHint = Surface.ROTATION_0;
     /**
      * Callback for notifying about global configuration changes.
      */
@@ -648,6 +651,7 @@
     /* Drag/drop */
     ClipDescription mDragDescription;
     View mCurrentDragView;
+    View mStartedDragViewForA11y;
     volatile Object mLocalDragState;
     final PointF mDragPoint = new PointF();
     final PointF mLastTouchPoint = new PointF();
@@ -4273,6 +4277,14 @@
         try {
             if (!isContentCaptureEnabled()) return;
 
+            // Initial dispatch of window bounds to content capture
+            if (mAttachInfo.mContentCaptureManager != null) {
+                MainContentCaptureSession session =
+                        mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
+                session.notifyWindowBoundsChanged(session.getId(),
+                        getConfiguration().windowConfiguration.getBounds());
+            }
+
             // Content capture is a go!
             rootView.dispatchInitialProvideContentCaptureStructure();
         } finally {
@@ -7573,6 +7585,11 @@
             if (what == DragEvent.ACTION_DRAG_STARTED) {
                 mCurrentDragView = null;    // Start the current-recipient tracking
                 mDragDescription = event.mClipDescription;
+                if (mStartedDragViewForA11y != null) {
+                    // Send a drag started a11y event
+                    mStartedDragViewForA11y.sendWindowContentChangedAccessibilityEvent(
+                            AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_STARTED);
+                }
             } else {
                 if (what == DragEvent.ACTION_DRAG_ENDED) {
                     mDragDescription = null;
@@ -7647,6 +7664,16 @@
 
                 // When the drag operation ends, reset drag-related state
                 if (what == DragEvent.ACTION_DRAG_ENDED) {
+                    if (mStartedDragViewForA11y != null) {
+                        // If the drag failed, send a cancelled event from the source. Otherwise,
+                        // the View that accepted the drop sends CONTENT_CHANGE_TYPE_DRAG_DROPPED
+                        if (!event.getResult()) {
+                            mStartedDragViewForA11y.sendWindowContentChangedAccessibilityEvent(
+                                    AccessibilityEvent.CONTENT_CHANGE_TYPE_DRAG_CANCELLED);
+                        }
+                        mStartedDragViewForA11y.setAccessibilityDragStarted(false);
+                    }
+                    mStartedDragViewForA11y = null;
                     mCurrentDragView = null;
                     setLocalDragState(null);
                     mAttachInfo.mDragToken = null;
@@ -7726,6 +7753,13 @@
         mCurrentDragView = newDragTarget;
     }
 
+    /** Sets the view that started drag and drop for the purpose of sending AccessibilityEvents */
+    void setDragStartedViewForAccessibility(View view) {
+        if (mStartedDragViewForA11y == null) {
+            mStartedDragViewForA11y = view;
+        }
+    }
+
     private AudioManager getAudioManager() {
         if (mView == null) {
             throw new IllegalStateException("getAudioManager called when there is no mView");
@@ -7804,6 +7838,14 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                 mTempControls, mSurfaceSize);
+
+        if (mAttachInfo.mContentCaptureManager != null) {
+            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+                    .getMainContentCaptureSession();
+            mainSession.notifyWindowBoundsChanged(mainSession.getId(),
+                    getConfiguration().windowConfiguration.getBounds());
+        }
+
         mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
         if (mSurfaceControl.isValid()) {
             if (!useBLAST()) {
@@ -7824,6 +7866,11 @@
                 }
                 mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
             }
+            int transformHint = mSurfaceControl.getTransformHint();
+            if (mPreviousTransformHint != transformHint) {
+                mPreviousTransformHint = transformHint;
+                dispatchTransformHintChanged(transformHint);
+            }
         } else {
             destroySurface();
         }
@@ -10447,7 +10494,39 @@
         return true;
     }
 
-    int getSurfaceTransformHint() {
+    @Override
+    public @Surface.Rotation int getSurfaceTransformHint() {
         return mSurfaceControl.getTransformHint();
     }
+
+    @Override
+    public void addOnSurfaceTransformHintChangedListener(
+            OnSurfaceTransformHintChangedListener listener) {
+        Objects.requireNonNull(listener);
+        if (mTransformHintListeners.contains(listener)) {
+            throw new IllegalArgumentException(
+                    "attempt to call addOnSurfaceTransformHintChangedListener() "
+                            + "with a previously registered listener");
+        }
+        mTransformHintListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnSurfaceTransformHintChangedListener(
+            OnSurfaceTransformHintChangedListener listener) {
+        Objects.requireNonNull(listener);
+        mTransformHintListeners.remove(listener);
+    }
+
+    private void dispatchTransformHintChanged(@Surface.Rotation int hint) {
+        if (mTransformHintListeners.isEmpty()) {
+            return;
+        }
+        ArrayList<OnSurfaceTransformHintChangedListener> listeners =
+                (ArrayList<OnSurfaceTransformHintChangedListener>) mTransformHintListeners.clone();
+        for (int i = 0; i < listeners.size(); i++) {
+            OnSurfaceTransformHintChangedListener listener = listeners.get(i);
+            listener.onSurfaceTransformHintChanged(hint);
+        }
+    }
 }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 0fc6b08..7631269 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -374,8 +374,8 @@
             currentDisplayInfo = possibleDisplayInfos.get(i);
 
             // Calculate max bounds for this rotation and state.
-            Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
-                    currentDisplayInfo.getNaturalHeight());
+            Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
+                    currentDisplayInfo.logicalHeight);
 
             // Calculate insets for the rotated max bounds.
             // TODO(181127261) calculate insets for each display rotation and state.
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index c413a9b..ffb7efa 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -498,4 +498,9 @@
     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
             RemoteCallback callback) {
     }
+
+    @Override
+    public boolean dropForAccessibility(IWindow window, int x, int y) {
+        return false;
+    }
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index b1d618e..81e2f3c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -258,8 +258,6 @@
 
         setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
 
-        setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0));
-
         setDetachWallpaper(
                 a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
         setShowWallpaper(
@@ -632,16 +630,15 @@
     }
 
     /**
-     * Set background behind animation.
+     * Set background behind an animation.
      *
-     * @param bg The background color.  If 0, no background.  Currently must
-     * be black, with any desired alpha level.
+     * @param bg The background color. If 0, no background.
      *
      * @deprecated None of window animations are running with background color.
      */
     @Deprecated
     public void setBackgroundColor(@ColorInt int bg) {
-        // The background color is not needed any more, do nothing.
+        mBackgroundColor = bg;
     }
 
     /**
@@ -803,7 +800,7 @@
     @Deprecated
     @ColorInt
     public int getBackgroundColor() {
-        return 0;
+        return mBackgroundColor;
     }
 
     /**
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index ec55a02..3365c70 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -57,6 +57,9 @@
     /** @hide */
     protected float mToYDelta;
 
+    private int mWidth;
+    private int mParentWidth;
+
     /**
      * Constructor used when a TranslateAnimation is loaded from a resource.
      *
@@ -179,5 +182,60 @@
         mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
         mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
         mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
+
+        mWidth = width;
+        mParentWidth = parentWidth;
+    }
+
+    /**
+     * Checks whether or not the translation is exclusively an x axis translation.
+     *
+     * @hide
+     */
+    public boolean isXAxisTransition() {
+        return mFromXDelta - mToXDelta != 0 && mFromYDelta - mToYDelta == 0;
+    }
+
+    /**
+     * Checks whether or not the translation is a full width x axis slide in or out translation.
+     *
+     * @hide
+     */
+    public boolean isFullWidthTranslate() {
+        boolean isXAxisSlideTransition =
+                isSlideInLeft() || isSlideOutRight() || isSlideInRight() || isSlideOutLeft();
+        return mWidth == mParentWidth && isXAxisSlideTransition;
+    }
+
+    private boolean isSlideInLeft() {
+        boolean startsOutOfParentOnLeft = mFromXDelta <= -mWidth;
+        return startsOutOfParentOnLeft && endsXEnclosedWithinParent();
+    }
+
+    private boolean isSlideOutRight() {
+        boolean endOutOfParentOnRight = mToXDelta >= mParentWidth;
+        return startsXEnclosedWithinParent() && endOutOfParentOnRight;
+    }
+
+    private boolean isSlideInRight() {
+        boolean startsOutOfParentOnRight = mFromXDelta >= mParentWidth;
+        return startsOutOfParentOnRight && endsXEnclosedWithinParent();
+    }
+
+    private boolean isSlideOutLeft() {
+        boolean endOutOfParentOnLeft = mToXDelta <= -mWidth;
+        return startsXEnclosedWithinParent() && endOutOfParentOnLeft;
+    }
+
+    private boolean endsXEnclosedWithinParent() {
+        return mWidth <= mParentWidth
+                && mToXDelta + mWidth <= mParentWidth
+                && mToXDelta >= 0;
+    }
+
+    private boolean startsXEnclosedWithinParent() {
+        return mWidth <= mParentWidth
+                && mFromXDelta + mWidth <= mParentWidth
+                && mFromXDelta >= 0;
     }
 }
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9998fbc..0da54e5 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.LocusId;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Display;
@@ -105,6 +106,7 @@
     private final int mFlags;
     private final int mDisplayId;
     private final ActivityId mActivityId;
+    private final IBinder mWindowToken;
 
     // Fields below are set by the service upon "delivery" and are not marshalled in the parcel
     private int mParentSessionId = NO_SESSION_ID;
@@ -112,7 +114,7 @@
     /** @hide */
     public ContentCaptureContext(@Nullable ContentCaptureContext clientContext,
             @NonNull ActivityId activityId, @NonNull ComponentName componentName, int displayId,
-            int flags) {
+            IBinder windowToken, int flags) {
         if (clientContext != null) {
             mHasClientContext = true;
             mExtras = clientContext.mExtras;
@@ -126,6 +128,7 @@
         mFlags = flags;
         mDisplayId = displayId;
         mActivityId = activityId;
+        mWindowToken = windowToken;
     }
 
     private ContentCaptureContext(@NonNull Builder builder) {
@@ -137,6 +140,7 @@
         mFlags = 0;
         mDisplayId = Display.INVALID_DISPLAY;
         mActivityId = null;
+        mWindowToken = null;
     }
 
     /** @hide */
@@ -148,6 +152,7 @@
         mFlags = original.mFlags | extraFlags;
         mDisplayId = original.mDisplayId;
         mActivityId = original.mActivityId;
+        mWindowToken = original.mWindowToken;
     }
 
     /**
@@ -230,6 +235,20 @@
     }
 
     /**
+     * Gets the window token of the activity associated with this context.
+     *
+     * <p>The token can be used to attach relevant overlay views to the activity's window. This can
+     * be done through {@link android.view.WindowManager.LayoutParams#token}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public IBinder getWindowToken() {
+        return mWindowToken;
+    }
+
+    /**
      * Gets the flags associated with this context.
      *
      * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE},
@@ -328,6 +347,7 @@
         }
         pw.print(", activityId="); pw.print(mActivityId);
         pw.print(", displayId="); pw.print(mDisplayId);
+        pw.print(", windowToken="); pw.print(mWindowToken);
         if (mParentSessionId != NO_SESSION_ID) {
             pw.print(", parentId="); pw.print(mParentSessionId);
         }
@@ -352,6 +372,7 @@
             builder.append("act=").append(ComponentName.flattenToShortString(mComponentName))
                 .append(", activityId=").append(mActivityId)
                 .append(", displayId=").append(mDisplayId)
+                .append(", windowToken=").append(mWindowToken)
                 .append(", flags=").append(mFlags);
         } else {
             builder.append("id=").append(mId);
@@ -381,6 +402,7 @@
         parcel.writeParcelable(mComponentName, flags);
         if (fromServer()) {
             parcel.writeInt(mDisplayId);
+            parcel.writeStrongBinder(mWindowToken);
             parcel.writeInt(mFlags);
             mActivityId.writeToParcel(parcel, flags);
         }
@@ -411,11 +433,12 @@
                 return clientContext;
             } else {
                 final int displayId = parcel.readInt();
+                final IBinder windowToken = parcel.readStrongBinder();
                 final int flags = parcel.readInt();
                 final ActivityId activityId = new ActivityId(parcel);
 
                 return new ContentCaptureContext(clientContext, activityId, componentName,
-                        displayId, flags);
+                        displayId, windowToken, flags);
             }
         }
 
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ce6d034..4b2d3a9 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -24,6 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.graphics.Insets;
+import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.Selection;
@@ -122,6 +123,12 @@
      */
     public static final int TYPE_VIEW_INSETS_CHANGED = 9;
 
+    /**
+     * Called before {@link #TYPE_VIEW_TREE_APPEARING}, or after the size of the window containing
+     * the views changed.
+     */
+    public static final int TYPE_WINDOW_BOUNDS_CHANGED = 10;
+
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_VIEW_APPEARED,
@@ -132,7 +139,8 @@
             TYPE_CONTEXT_UPDATED,
             TYPE_SESSION_PAUSED,
             TYPE_SESSION_RESUMED,
-            TYPE_VIEW_INSETS_CHANGED
+            TYPE_VIEW_INSETS_CHANGED,
+            TYPE_WINDOW_BOUNDS_CHANGED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -150,6 +158,7 @@
     private int mParentSessionId = NO_SESSION_ID;
     private @Nullable ContentCaptureContext mClientContext;
     private @Nullable Insets mInsets;
+    private @Nullable Rect mBounds;
 
     private int mComposingStart = MAX_INVALID_VALUE;
     private int mComposingEnd = MAX_INVALID_VALUE;
@@ -346,6 +355,13 @@
         return this;
     }
 
+    /** @hide */
+    @NonNull
+    public ContentCaptureEvent setBounds(@NonNull Rect bounds) {
+        mBounds = bounds;
+        return this;
+    }
+
     /**
      * Gets the type of the event.
      *
@@ -419,6 +435,16 @@
     }
 
     /**
+     * Gets the {@link Rect} bounds of the window associated with the event. Valid bounds will only
+     * be returned if the type of the event is {@link #TYPE_WINDOW_BOUNDS_CHANGED}, otherwise they
+     * will be null.
+     */
+    @Nullable
+    public Rect getBounds() {
+        return mBounds;
+    }
+
+    /**
      * Merges event of the same type, either {@link #TYPE_VIEW_TEXT_CHANGED}
      * or {@link #TYPE_VIEW_DISAPPEARED}.
      *
@@ -489,6 +515,9 @@
         if (mInsets != null) {
             pw.print(", insets="); pw.println(mInsets);
         }
+        if (mBounds != null) {
+            pw.print(", bounds="); pw.println(mBounds);
+        }
         if (mComposingStart > MAX_INVALID_VALUE) {
             pw.print(", composing("); pw.print(mComposingStart);
             pw.print(", "); pw.print(mComposingEnd); pw.print(")");
@@ -533,6 +562,9 @@
         if (mInsets != null) {
             string.append(", insets=").append(mInsets);
         }
+        if (mBounds != null) {
+            string.append(", bounds=").append(mBounds);
+        }
         if (mComposingStart > MAX_INVALID_VALUE) {
             string.append(", composing=[")
                     .append(mComposingStart).append(",").append(mComposingEnd).append("]");
@@ -568,6 +600,9 @@
         if (mType == TYPE_VIEW_INSETS_CHANGED) {
             parcel.writeParcelable(mInsets, flags);
         }
+        if (mType == TYPE_WINDOW_BOUNDS_CHANGED) {
+            parcel.writeParcelable(mBounds, flags);
+        }
         if (mType == TYPE_VIEW_TEXT_CHANGED) {
             parcel.writeInt(mComposingStart);
             parcel.writeInt(mComposingEnd);
@@ -608,6 +643,9 @@
             if (type == TYPE_VIEW_INSETS_CHANGED) {
                 event.setInsets(parcel.readParcelable(null));
             }
+            if (type == TYPE_WINDOW_BOUNDS_CHANGED) {
+                event.setBounds(parcel.readParcelable(null));
+            }
             if (type == TYPE_VIEW_TEXT_CHANGED) {
                 event.setComposingIndex(parcel.readInt(), parcel.readInt());
                 event.restoreComposingSpan();
@@ -649,6 +687,8 @@
                 return "CONTEXT_UPDATED";
             case TYPE_VIEW_INSETS_CHANGED:
                 return "VIEW_INSETS_CHANGED";
+            case TYPE_WINDOW_BOUNDS_CHANGED:
+                return "TYPE_WINDOW_BOUNDS_CHANGED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 4cf5532..98ef4e7 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -26,6 +26,7 @@
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_WINDOW_BOUNDS_CHANGED;
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
@@ -38,6 +39,7 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Insets;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -776,6 +778,14 @@
                 .setClientContext(context), FORCE_FLUSH));
     }
 
+    /** public because is also used by ViewRootImpl */
+    public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+        mHandler.post(() -> sendEvent(
+                new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
+                .setBounds(bounds)
+        ));
+    }
+
     @Override
     void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
         super.dump(prefix, pw);
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index eca7481..60402eb 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -593,9 +593,8 @@
             final View rootView = roots.get(rootNum).getView();
             if (rootView instanceof ViewGroup) {
                 findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds);
-            } else {
-                addViewIfNeeded(sourceViewIds, rootView);
             }
+            addViewIfNeeded(sourceViewIds, rootView);
         }
     }
 
@@ -606,9 +605,8 @@
             final View child = viewGroup.getChildAt(i);
             if (child instanceof ViewGroup) {
                 findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds);
-            } else {
-                addViewIfNeeded(sourceViewIds, child);
             }
+            addViewIfNeeded(sourceViewIds, child);
         }
     }
 
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index a075662..66c028b 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -41,6 +41,11 @@
      * method will not be called before {@link View#onViewTranslationResponse} or
      * {@link View#onVirtualViewTranslationResponses}.
      *
+     * <p> NOTE: It is possible the user changes text that causes a new
+     * {@link ViewTranslationResponse} returns to show the new translation. If you cache the
+     * {@link ViewTranslationResponse} here, you should remember to keep the cached value up
+     * to date.
+     *
      * <p> NOTE: For TextView implementation, {@link ContentCaptureSession#notifyViewTextChanged}
      * shouldn't be called with the translated text, simply calling setText() here will trigger the
      * method. You should either override {@code View#onProvideContentCaptureStructure()} to report
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 9d60009..152405b 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -70,7 +70,12 @@
                     + "onViewTranslationResponse().");
             return false;
         }
-        if (mTranslationTransformation == null) {
+        // It is possible user changes text and new translation response returns, system should
+        // update the translation response to keep the result up to date.
+        // Because TextView.setTransformationMethod() will skip the same TransformationMethod
+        // instance, we should create a new one to let new translation can work.
+        if (mTranslationTransformation == null
+                || !response.equals(mTranslationTransformation.getViewTranslationResponse())) {
             TransformationMethod originalTranslationMethod =
                     ((TextView) view).getTransformationMethod();
             mTranslationTransformation = new TranslationTransformationMethod(response,
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index 84354d9..ec224e5 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -72,17 +72,19 @@
     private Resources mSuspendingAppResources;
     private SuspendDialogInfo mSuppliedDialogInfo;
     private Bundle mOptions;
-    private BroadcastReceiver mUnsuspendReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mSuspendModifiedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(intent.getAction())) {
-                final String[] unsuspended = intent.getStringArrayExtra(
+            if (Intent.ACTION_PACKAGES_SUSPENSION_CHANGED.equals(intent.getAction())) {
+                // Suspension conditions were modified, dismiss any related visible dialogs.
+                final String[] modified = intent.getStringArrayExtra(
                         Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                if (ArrayUtils.contains(unsuspended, mSuspendedPackage)) {
+                if (ArrayUtils.contains(modified, mSuspendedPackage)) {
                     if (!isFinishing()) {
-                        Slog.w(TAG, "Package " + mSuspendedPackage
-                                + " got unsuspended while the dialog was visible. Finishing.");
+                        Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+                                + " suspension conditions while dialog was visible. Finishing.");
                         SuspendedAppActivity.this.finish();
+                        // TODO (b/198201994): reload the suspend dialog to show most relevant info
                     }
                 }
             }
@@ -245,15 +247,16 @@
 
         setupAlert();
 
-        final IntentFilter unsuspendFilter = new IntentFilter(Intent.ACTION_PACKAGES_UNSUSPENDED);
-        registerReceiverAsUser(mUnsuspendReceiver, UserHandle.of(mUserId), unsuspendFilter, null,
-                null);
+        final IntentFilter suspendModifiedFilter =
+                new IntentFilter(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED);
+        registerReceiverAsUser(mSuspendModifiedReceiver, UserHandle.of(mUserId),
+                suspendModifiedFilter, null, null);
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        unregisterReceiver(mUnsuspendReceiver);
+        unregisterReceiver(mSuspendModifiedReceiver);
     }
 
     private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7e286f0..a817119 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8618,7 +8618,7 @@
          * inactive so can be dropped.
          */
         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-        public boolean reset(long uptimeUs, long realtimeUs) {
+        public boolean reset(long uptimeUs, long realtimeUs, int resetReason) {
             boolean active = false;
 
             mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
@@ -8688,7 +8688,11 @@
             resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
             resetIfNotNull(mModemControllerActivity, false, realtimeUs);
 
-            MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+            if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
+                mUidMeasuredEnergyStats = null;
+            } else {
+                MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+            }
 
             resetIfNotNull(mUserCpuTime, false, realtimeUs);
             resetIfNotNull(mSystemCpuTime, false, realtimeUs);
@@ -11371,7 +11375,7 @@
         mNumConnectivityChange = 0;
 
         for (int i=0; i<mUidStats.size(); i++) {
-            if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs)) {
+            if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs, resetReason)) {
                 mUidStats.valueAt(i).detachFromTimeBase();
                 mUidStats.remove(mUidStats.keyAt(i));
                 i--;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 0038579..980aec1 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -29,6 +29,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -234,8 +235,9 @@
         final boolean includePowerModels = (query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
 
+        final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
         final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
-                mStats.getCustomEnergyConsumerNames(), includePowerModels);
+                customEnergyConsumerNames, includePowerModels);
         if (mBatteryUsageStatsStore == null) {
             Log.e(TAG, "BatteryUsageStatsStore is unavailable");
             return builder.build();
@@ -247,7 +249,14 @@
                 final BatteryUsageStats snapshot =
                         mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
                 if (snapshot != null) {
-                    builder.add(snapshot);
+                    if (Arrays.equals(snapshot.getCustomPowerComponentNames(),
+                            customEnergyConsumerNames)) {
+                        builder.add(snapshot);
+                    } else {
+                        Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
+                                + "custom power components: "
+                                + Arrays.toString(snapshot.getCustomPowerComponentNames()));
+                    }
                 }
             }
         }
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 9b51a8e..bb307a0 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -21,13 +21,18 @@
 import android.os.BatteryUsageStats;
 import android.os.BatteryUsageStatsQuery;
 import android.os.UidBatteryConsumer;
+import android.util.Slog;
 import android.util.SparseArray;
 
+import java.util.Arrays;
+
 /**
  * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
  * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
  */
 public class CustomMeasuredPowerCalculator extends PowerCalculator {
+    private static final String TAG = "CustomMeasuredPowerCalc";
+
     public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
     }
 
@@ -76,9 +81,9 @@
             if (totalPowerMah == null) {
                 newTotalPowerMah = new double[customMeasuredPowerMah.length];
             } else if (totalPowerMah.length != customMeasuredPowerMah.length) {
-                newTotalPowerMah = new double[customMeasuredPowerMah.length];
-                System.arraycopy(totalPowerMah, 0, newTotalPowerMah, 0,
-                        customMeasuredPowerMah.length);
+                Slog.wtf(TAG, "Number of custom energy components is not the same for all apps: "
+                        + totalPowerMah.length + ", " + customMeasuredPowerMah.length);
+                newTotalPowerMah = Arrays.copyOf(totalPowerMah, customMeasuredPowerMah.length);
             } else {
                 newTotalPowerMah = totalPowerMah;
             }
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index b321ac0..a09c823 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -124,7 +124,6 @@
                         Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                         try {
-                            sendCloseSystemWindows();
                             mContext.startActivity(intent);
                         } catch (ActivityNotFoundException e) {
                             startCallActivity();
@@ -147,7 +146,6 @@
                     dispatcher.performedLongPress(event);
                     if (isUserSetupComplete()) {
                         mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                        sendCloseSystemWindows();
                         // Broadcast an intent that the Camera button was longpressed
                         Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
                         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -178,7 +176,6 @@
                             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                             try {
                                 mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                                sendCloseSystemWindows();
                                 getSearchManager().stopSearch();
                                 mContext.startActivity(intent);
                                 // Only clear this if we successfully start the
@@ -272,7 +269,6 @@
 
     @UnsupportedAppUsage
     void startCallActivity() {
-        sendCloseSystemWindows();
         Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
@@ -319,10 +315,6 @@
         return mMediaSessionManager;
     }
 
-    void sendCloseSystemWindows() {
-        PhoneWindow.sendCloseSystemWindows(mContext, null);
-    }
-
     private void handleVolumeKeyEvent(KeyEvent keyEvent) {
         getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent,
                 AudioManager.USE_DEFAULT_STREAM_TYPE);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 50cd70f..5ce43df 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -870,6 +870,13 @@
     transaction->setFixedTransformHint(ctrl, transformHint);
 }
 
+static void nativeSetDropInputMode(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                   jlong nativeObject, jint mode) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    transaction->setDropInputMode(ctrl, static_cast<gui::DropInputMode>(mode));
+}
+
 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     jlongArray array = env->NewLongArray(displayIds.size());
@@ -891,6 +898,12 @@
     return array;
 }
 
+static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) {
+    PhysicalDisplayId displayId;
+    SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId);
+    return static_cast<jlong>(displayId.value);
+}
+
 static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
     sp<IBinder> token =
             SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
@@ -1892,6 +1905,8 @@
             (void*)nativeReleaseFrameRateFlexibilityToken },
     {"nativeGetPhysicalDisplayIds", "()[J",
             (void*)nativeGetPhysicalDisplayIds },
+    {"nativeGetPrimaryPhysicalDisplayId", "()J",
+            (void*)nativeGetPrimaryPhysicalDisplayId },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
             (void*)nativeGetPhysicalDisplayToken },
     {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
@@ -2009,6 +2024,8 @@
             (void*)nativeGetTransformHint },
     {"nativeSetTrustedOverlay", "(JJZ)V",
             (void*)nativeSetTrustedOverlay },
+    {"nativeSetDropInputMode", "(JJI)V",
+             (void*)nativeSetDropInputMode },
         // clang-format on
 };
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 63d61fc..3f530fe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -55,6 +55,7 @@
     <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENSION_CHANGED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY" />
     <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
     <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
diff --git a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
index 5add19b..aa380006 100644
--- a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
+++ b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml
@@ -18,19 +18,9 @@
 -->
 <!-- This should be kept in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:hasRoundedCorners="true"
      android:shareInterpolator="false"
-     android:zAdjustment="top">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="-105%"
@@ -38,36 +28,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- To keep the thumbnail around longer and fade out the thumbnail -->
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
diff --git a/core/res/res/anim-ldrtl/task_close_enter.xml b/core/res/res/anim-ldrtl/task_close_enter.xml
index e00141a..5ace46d 100644
--- a/core/res/res/anim-ldrtl/task_close_enter.xml
+++ b/core/res/res/anim-ldrtl/task_close_enter.xml
@@ -14,20 +14,9 @@
   ~ limitations under the License
   -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="105%"
@@ -35,34 +24,7 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml
index 71a44ae..76fbdff 100644
--- a/core/res/res/anim-ldrtl/task_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_close_exit.xml
@@ -15,19 +15,8 @@
   -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="1"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="283"/>
+     android:shareInterpolator="false"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="0"
@@ -35,22 +24,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0"
-        android:toXScale="0.95"
-        android:fromYScale="1.0"
-        android:toYScale="0.95"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha
diff --git a/core/res/res/anim-ldrtl/task_open_enter.xml b/core/res/res/anim-ldrtl/task_open_enter.xml
index 7815f7d..52c74a6 100644
--- a/core/res/res/anim-ldrtl/task_open_enter.xml
+++ b/core/res/res/anim-ldrtl/task_open_enter.xml
@@ -16,20 +16,9 @@
 <!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
 <!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="-105%"
@@ -37,34 +26,7 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
-</set>
\ No newline at end of file
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
index 5fccd6df..90ec071 100644
--- a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
@@ -16,20 +16,9 @@
   -->
 <!-- This should in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="-105%"
@@ -37,36 +26,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
          cross_profile_apps_thumbmail.xml -->
@@ -75,4 +37,4 @@
         android:toAlpha="1.0"
         android:startOffset="717"
         android:duration="200"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml
index 025e1bd..beb6fca 100644
--- a/core/res/res/anim-ldrtl/task_open_exit.xml
+++ b/core/res/res/anim-ldrtl/task_open_exit.xml
@@ -15,19 +15,8 @@
   -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="1"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="283"/>
+     android:shareInterpolator="false"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="0"
@@ -35,22 +24,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0"
-        android:toXScale="0.95"
-        android:fromYScale="1.0"
-        android:toYScale="0.95"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha
diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
index 2cfeecf..f6d7b72 100644
--- a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
+++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
@@ -19,18 +19,8 @@
 <!-- This should be kept in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:shareInterpolator="false"
-     android:hasRoundedCorners="true"
-     android:zAdjustment="top">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="105%"
@@ -38,36 +28,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- To keep the thumbnail around longer and fade out the thumbnail -->
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 487ff5c..52017b1 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -16,20 +16,9 @@
 */
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="-105%"
@@ -37,34 +26,7 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index afc3256c..736f3f2 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -17,19 +17,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="1"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="283"/>
+     android:shareInterpolator="false"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="0"
@@ -37,22 +26,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0"
-        android:toXScale="0.95"
-        android:fromYScale="1.0"
-        android:toYScale="0.95"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 0aafc1c..3c93438 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -15,23 +15,12 @@
 ** limitations under the License.
 */
 -->
-<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
-<!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
+<!-- This should be kept in sync with task_open_enter_cross_profile_apps.xml -->
+<!-- This should be kept in sync with cross_profile_apps_thumbnail_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="105%"
@@ -39,34 +28,7 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
index 702f7ba..16249d1 100644
--- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -18,20 +18,9 @@
 -->
 <!-- This should in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:zAdjustment="top"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1"
-        android:toAlpha="1.0"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="217"/>
+     android:shareInterpolator="false"
+     android:zAdjustment="top"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="105%"
@@ -39,36 +28,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0526"
-        android:toXScale="1"
-        android:fromYScale="1.0526"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
-
-    <scale
-        android:fromXScale="0.95"
-        android:toXScale="1"
-        android:fromYScale="0.95"
-        android:toYScale="1"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:startOffset="283"
-        android:duration="317"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
          cross_profile_apps_thumbmail.xml -->
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 691317d..d170317 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -17,19 +17,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false"
-    android:hasRoundedCorners="true"
-    android:showWallpaper="true">
-
-    <alpha
-        android:fromAlpha="1.0"
-        android:toAlpha="1"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:interpolator="@interpolator/linear"
-        android:startOffset="67"
-        android:duration="283"/>
+     android:shareInterpolator="false"
+     android:hasRoundedCorners="true">
 
     <translate
         android:fromXDelta="0"
@@ -37,22 +26,9 @@
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
-        android:interpolator="@interpolator/aggressive_ease"
-        android:startOffset="50"
-        android:duration="383"/>
-
-    <scale
-        android:fromXScale="1.0"
-        android:toXScale="0.95"
-        android:fromYScale="1.0"
-        android:toYScale="0.95"
-        android:fillEnabled="true"
-        android:fillBefore="true"
-        android:fillAfter="true"
-        android:pivotX="50%"
-        android:pivotY="50%"
-        android:interpolator="@interpolator/fast_out_slow_in"
-        android:duration="283"/>
+        android:interpolator="@interpolator/fast_out_extra_slow_in"
+        android:startOffset="0"
+        android:duration="500"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
     <alpha
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml b/core/res/res/color/overview_background.xml
similarity index 60%
copy from libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
copy to core/res/res/color/overview_background.xml
index 9115b5a..45c6c25 100644
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
+++ b/core/res/res/color/overview_background.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2021 The Android Open Source Project
   ~
@@ -13,14 +14,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/split_divider_corner_size"
-        android:height="@dimen/split_divider_corner_size"
-        android:viewportWidth="42"
-        android:viewportHeight="42">
-
-    <path
-        android:fillColor="@color/split_divider_background"
-        android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-
-</vector>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral2_500" android:lStar="87" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml b/core/res/res/color/overview_background_dark.xml
similarity index 60%
rename from libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
rename to core/res/res/color/overview_background_dark.xml
index 9115b5a..84f4fdf 100644
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
+++ b/core/res/res/color/overview_background_dark.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2021 The Android Open Source Project
   ~
@@ -13,14 +14,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/split_divider_corner_size"
-        android:height="@dimen/split_divider_corner_size"
-        android:viewportWidth="42"
-        android:viewportHeight="42">
-
-    <path
-        android:fillColor="@color/split_divider_background"
-        android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-
-</vector>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-nodpi/default_wallpaper.png b/core/res/res/drawable-nodpi/default_wallpaper.png
index 490ebee..5152972 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
new file mode 100644
index 0000000..26376fb
--- /dev/null
+++ b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
new file mode 100644
index 0000000..490ebee
--- /dev/null
+++ b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index 8a3d734..3259201 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -40,6 +40,5 @@
         android:maxLines="2"
         android:paddingTop="12dp"
         android:paddingBottom="12dp"
-        android:lineHeight="20sp"
         android:textAppearance="@style/TextAppearance.Toast"/>
 </LinearLayout>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 917a80c..8aafa0a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -34,7 +34,7 @@
     <string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
     <string name="mmiError" msgid="2862759606579822246">"সংযোগৰ সমস্যা বা MMI ক\'ড মান্য নহয়।"</string>
     <string name="mmiFdnError" msgid="3975490266767565852">"কেৱল ফিক্সড ডায়েলিং নম্বৰৰ বাবে কার্য সীমাবদ্ধ কৰা হৈছে।"</string>
-    <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিংসমূহ সলনি কৰিব নোৱাৰি।"</string>
+    <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"আপুনি ৰ\'মিঙত থকাৰ সময়ত কল ফৰৱাৰ্ডিঙৰ ছেটিং সলনি কৰিব নোৱাৰি।"</string>
     <string name="serviceEnabled" msgid="7549025003394765639">"সেৱা সক্ষম কৰা হ’ল।"</string>
     <string name="serviceEnabledFor" msgid="1463104778656711613">"সেৱা সক্ষম কৰা হ’ল:"</string>
     <string name="serviceDisabled" msgid="641878791205871379">"সেৱা অক্ষম কৰা হ’ল।"</string>
@@ -124,7 +124,7 @@
     <string name="roamingTextSearching" msgid="5323235489657753486">"সেৱাৰ বাবে অনুসন্ধান কৰি থকা হৈছে"</string>
     <string name="wfcRegErrorTitle" msgid="3193072971584858020">"ৱাই-ফাই কলিং ছেট আপ কৰিব পৰা নগ\'ল"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"ৱাই-ফাইৰ জৰিয়তে কল কৰিবলৈ আৰু বাৰ্তা পঠাবলৈ, প্ৰথমে আপোনাৰ বাহকক আপোনাৰ ডিভাইচটো ছেট আপ কৰিব দিবলৈ কওক। তাৰ পিচত, ছেটিংসমূহলৈ গৈ আকৌ ৱাই-ফাই কলিং অন কৰক। (ত্ৰুটি ক\'ড: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"ৱাই-ফাইৰ জৰিয়তে কল কৰিবলৈ আৰু বাৰ্তা পঠাবলৈ, প্ৰথমে আপোনাৰ বাহকক আপোনাৰ ডিভাইচটো ছেট আপ কৰিবলৈ কওক। তাৰ পাছত, ছেটিঙলৈ গৈ আকৌ ৱাই-ফাই কলিং অন কৰক। (ত্ৰুটি ক\'ড: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"আপোনাৰ বাহকৰ ওচৰত ৱাই-ফাই কলিং সুবিধা পঞ্জীয়ন কৰাত সমস্যাৰ উদ্ভৱ হৈছে: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -218,7 +218,7 @@
     <string name="silent_mode" msgid="8796112363642579333">"নিঃশব্দ ম\'ড"</string>
     <string name="turn_on_radio" msgid="2961717788170634233">"ৱায়াৰলেছ অন কৰক"</string>
     <string name="turn_off_radio" msgid="7222573978109933360">"ৱায়াৰলেছ অফ কৰক"</string>
-    <string name="screen_lock" msgid="2072642720826409809">"স্ক্ৰীণ লক"</string>
+    <string name="screen_lock" msgid="2072642720826409809">"স্ক্ৰীন লক"</string>
     <string name="power_off" msgid="4111692782492232778">"পাৱাৰ অফ"</string>
     <string name="silent_mode_silent" msgid="5079789070221150912">"ৰিংগাৰ অফ আছে"</string>
     <string name="silent_mode_vibrate" msgid="8821830448369552678">"ৰিংগাৰ কম্পন অৱস্থাত আছে"</string>
@@ -268,7 +268,7 @@
     <string name="global_actions_toggle_airplane_mode" msgid="6911684460146916206">"এয়াৰপ্লেইন ম\'ড"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="5508025516695361936">"এয়াৰপ্লেইন ম\'ড অন কৰা আছে"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="8522219771500505475">"এয়াৰপ্লেইন ম\'ড অফ কৰা আছে"</string>
-    <string name="global_action_settings" msgid="4671878836947494217">"ছেটিংসমূহ"</string>
+    <string name="global_action_settings" msgid="4671878836947494217">"ছেটিং"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"সহায়"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"কণ্ঠধ্বনিৰে সহায়"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"লকডাউন"</string>
@@ -400,8 +400,8 @@
     <string name="permdesc_foregroundService" msgid="8720071450020922795">"এপটোক অগ্ৰভূমি সেৱাসমূহ ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="permlab_getPackageSize" msgid="375391550792886641">"এপৰ সঞ্চয়াগাৰৰ খালী ঠাই হিচাপ কৰক"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"এপটোক ইয়াৰ ক\'ড, ডেটা আৰু কেশ্বৰ আকাৰ বিচাৰি উলিয়াবলৈ অনুমতি দিয়ে"</string>
-    <string name="permlab_writeSettings" msgid="8057285063719277394">"ছিষ্টেম ছেটিংসমূহ সংশোধন কৰক"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"এপটোক ছিষ্টেমৰ ছেটিংসমূহৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ ক্ষতিকাৰক এপসমূহে আপোনাৰ ছিষ্টেম কনফিগাৰেশ্বনক ক্ষতিগ্ৰস্ত কৰিব পাৰে৷"</string>
+    <string name="permlab_writeSettings" msgid="8057285063719277394">"ছিষ্টেম ছেটিংহ সংশোধন কৰক"</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"এপ্‌টোক ছিষ্টেমৰ ছেটিঙৰ ডেটা সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ ক্ষতিকাৰক এপ্‌সমূহে আপোনাৰ ছিষ্টেম কনফিগাৰেশ্বনক ক্ষতিগ্ৰস্ত কৰিব পাৰে৷"</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"আৰম্ভ হোৱাৰ সময়ত চলাওক"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"ছিষ্টেমে বুট কৰা কাৰ্য সমাপ্ত কৰাৰ লগে লগে এপটোক নিজে নিজে আৰম্ভ হ\'বলৈ অনুমতি দিয়ে। ইয়াৰ ফলত ফ\'নটো ষ্টাৰ্ট হওতে বেছি সময়ৰ প্ৰয়োজন হ\'ব পাৰে, আৰু এপটো সদায় চলি থকাৰ কাৰণে ফ\'নটো সামগ্ৰিকভাৱে লেহেমীয়া হ\'ব পাৰে।"</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"ছিষ্টেমে বুটিং সমাপ্ত কৰাৰ লগে লগে এই এপ্‌টোক নিজে নিজে আৰম্ভ হ’বলৈ অনুমতি দিয়ে। এই কাৰ্যৰ বাবে আপোনাৰ Android TV ডিভাইচটো আৰম্ভ হ’বলৈ দীঘলীয়া সময়ৰ প্ৰয়োজন হ’ব পাৰে আৰু সকলো সময়তে চলি থাকি এপ্‌টোক সামগ্ৰিকভাৱে ডিভাইচটো লেহেমীয়া কৰিবলৈ দিয়ে।"</string>
@@ -442,8 +442,8 @@
     <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"এই এপ্‌টো ব্যৱহাৰ হৈ থকা অৱস্থাত ই অৱস্থান সেৱাসমূহৰ পৰা আপোনাৰ আনুমানিক অৱস্থান লাভ কৰিব পাৰে। এপ্‌টোৱে অৱস্থান লাভ কৰিবলৈ হ’লে আপোনাৰ ডিভাইচৰ অৱস্থান সেৱাসমূহ অন কৰি ৰাখিবই লাগিব।"</string>
     <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"নেপথ্যত চলি থকা সময়ত অৱস্থানৰ এক্সেছ"</string>
     <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"এই এপ্‌টোৱে যিকোনো সময়তে অৱস্থান এক্সেছ কৰিব পাৰে, আনকি এপ্‌টো ব্যৱহাৰ হৈ নথকা অৱস্থাতো।"</string>
-    <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপোনাৰ অডিঅ\' ছেটিংসমূহ সলনি কৰক"</string>
-    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"এপটোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিংসমূহ যাৰ স্পীকাৰক আউটপুটৰ বাবে ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
+    <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"আপোনাৰ অডিঅ\' ছেটিং সলনি কৰক"</string>
+    <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"এপ্‌টোক ভলিউমৰ দৰে গ্ল\'বেল অডিঅ\' ছেটিং আৰু আউটপুটৰ বাবে কোনটো স্পীকাৰ ব্যৱহাৰ হয় তাক সলনি কৰিবলৈ অনুমতি দিয়ে৷"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"অডিঅ\' ৰেকর্ড কৰক"</string>
     <string name="permdesc_recordAudio" msgid="5857246765327514062">"এই এপ্‌টোৱে ইয়াক ব্যৱহাৰ কৰি থাকোঁতে মাইক্ৰ’ফ’ন ব্যৱহাৰ কৰি অডিঅ’ ৰেকর্ড কৰিব পাৰে।"</string>
     <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"নেপথ্যত অডিঅ’ ৰেকৰ্ড কৰক"</string>
@@ -519,7 +519,7 @@
     <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"আপোনাৰ টেবলেটৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।"</string>
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"কেৱল আপোনাৰ Android TV ডিভাইচটোৱেই নহয়, মাল্টিকাষ্ট ঠিকনাবোৰ ব্যৱহাৰ কৰি এটা ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেটবোৰ লাভ কৰিবলৈ এপ্‌টোক অনুমতি দিয়ে। এই কার্যই ন’ন-মাল্টিকাষ্ট ম’ডতকৈ অধিক পাৱাৰ ব্যৱহাৰ কৰে।"</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"আপোনাৰ ফ\'নৰ লগতে মাল্টিকাষ্ট ঠিকনাবোৰ ও ব্যৱহাৰ কৰি এপক ৱাই-ফাই নেটৱর্কত থকা সকলো ডিভাইচলৈ পঠোৱা পেকেট প্ৰাপ্ত কৰিবলৈ অনুমতি দিয়ে। এই কার্যই ন\'ন মাল্টিকাষ্ট ম\'ডতকৈ বেটাৰিৰ অধিক চ্চাৰ্জ ব্যৱহাৰ কৰে।"</string>
-    <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ব্লুটুথ ছেটিংসমূহ ব্যৱহাৰ কৰক"</string>
+    <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ব্লুটুথ ছেটিং এক্সেছ কৰক"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"স্থানীয় ব্লুটুথ টে\'বলেট কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।"</string>
     <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"এপ্‌টোক আপোনাৰ Android TV ডিভাইচটোত ব্লুটুথ কনফিগাৰ কৰিবলৈ আৰু ৰিম’ট ডিভাইচসমূহ বিচাৰি উলিয়াবলৈ আৰু পেয়াৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"স্থানীয় ব্লুটুথ ফ\'ন কনফিগাৰ কৰিবলৈ আৰু দূৰৱৰ্তী ডিভাইচসমূহৰ সৈতে যোৰা লগাবলৈ আৰু বিচাৰি উলিয়াবলৈ এপটোক অনুমতি দিয়ে।"</string>
@@ -547,7 +547,7 @@
     <string name="permdesc_nfc" msgid="8352737680695296741">"এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"আপোনাৰ স্ক্ৰীন লক অক্ষম কৰক"</string>
     <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
-    <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্ৰীণ লকৰ জটিলতাৰ অনুৰোধ"</string>
+    <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্ৰীন লকৰ জটিলতাৰ অনুৰোধ"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"এপ্‌টোক স্ক্ৰীন লকৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই)ৰ বিষয়ে জানিবলৈ অনুমতি দিয়ে, যিয়ে স্ক্ৰীন লকৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীন লকৰ প্ৰকাৰ দৰ্শায়। লগতে এপ্‌টোৱে ব্যৱহাৰকাৰীক স্ক্ৰীন লকটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে অৱজ্ঞা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীন লকটো সাধাৰণ পাঠ হিচাপে ষ্ট\'ৰ কৰা নহয়; সেয়েহে, এপ্‌টোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।"</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক"</string>
     <string name="permdesc_useBiometric" msgid="7502858732677143410">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
@@ -640,7 +640,7 @@
     <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"আপোনাৰ মূৰটো অলপ কমকৈ হেলনীয়া কৰক।"</string>
     <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"আপোনাৰ মূৰটো সামান্য কমকৈ ঘূৰাওক।"</string>
     <string name="face_acquired_obscured" msgid="4917643294953326639">"আপোনাৰ মুখখন ঢাকি ৰখা বস্তুবোৰ আঁতৰাওক।"</string>
-    <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ক’লা বাৰডালকে ধৰি আপোনাৰ স্ক্রীণৰ ওপৰৰ অংশ চাফা কৰক"</string>
+    <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ক’লা বাৰডালকে ধৰি আপোনাৰ স্ক্রীনৰ ওপৰৰ অংশ চাফা কৰক"</string>
   <string-array name="face_acquired_vendor">
   </string-array>
     <string name="face_error_hw_not_available" msgid="5085202213036026288">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। হাৰ্ডৱেৰ নাই।"</string>
@@ -664,8 +664,8 @@
   </string-array>
     <string name="face_error_vendor_unknown" msgid="7387005932083302070">"কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।"</string>
     <string name="face_icon_content_description" msgid="465030547475916280">"মুখমণ্ডলৰ আইকন"</string>
-    <string name="permlab_readSyncSettings" msgid="6250532864893156277">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
-    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
+    <string name="permlab_readSyncSettings" msgid="6250532864893156277">"ছিংকৰ ছেটিং পঢ়ক"</string>
+    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ্‌টো কোনো একাউণ্টৰ সৈতে ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
     <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
     <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"এপটোক কোনো একাউণ্টৰ ছিংক সম্পৰ্কীয় ছেটিংসমূহ সংশোধন কৰিবলৈ অনুমতি দিয়ে৷ উদাহৰণস্বৰূপে, এই কাৰ্যক কোনো একাউণ্টৰ জৰিয়তে People এপটোৰ ছিংক সক্ষম কৰিবলৈ ব্যৱহাৰ কৰিব পাৰি৷"</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"ছিংকৰ পৰিসংখ্যা পঢ়ক"</string>
@@ -727,9 +727,9 @@
     <string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string>
     <string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্ৰীন লক পাছৱৰ্ড আৰু পিনত অনুমোদিত দৈৰ্ঘ্য আৰু বৰ্ণবোৰ নিয়ন্ত্ৰণ কৰক।।"</string>
     <string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীন আনলক কৰা প্ৰয়াসবোৰ নিৰীক্ষণ কৰক"</string>
-    <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে টে\'বলেটটো লক কৰক বা টে\'বলেটটোৰ সকলো ডেটা মোহাৰক।"</string>
-    <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীণ আনলক কৰোঁতে দিয়া ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড দিয়া হয় তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ সকলো ডেটা মচক।"</string>
-    <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীণ আনলক কৰোতে লিখা অশুদ্ধ পাছৱৰ্ডৰ হিচাপ ৰাখক, আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড লিখা হয় তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ সকলো ডেটা মোহাৰক।"</string>
+    <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা টেবলেটটোৰ আটাইখিনি ডেটা মচক।"</string>
+    <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা আপোনাৰ Android TV ডিভাইচৰ আটাইখিনি ডেটা মচক।"</string>
+    <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যাধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক বা ফ\'নটোৰ আটাইখিনি ডেটা মচক।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"স্ক্ৰীন আনলক কৰোঁতে টাইপ কৰা অশুদ্ধ পাছৱৰ্ডৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ অশুদ্ধ পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে টেবলেটটো লক কৰক বা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে Android TV ডিভাইচটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"স্ক্ৰীনখন আনলক কৰোঁতে টাইপ কৰা ভুল পাছৱৰ্ডবোৰৰ সংখ্যা নিৰীক্ষণ কৰক আৰু যদিহে অত্যধিকবাৰ ভুল পাছৱৰ্ড টাইপ কৰা হয়, তেন্তে ফ\'নটো লক কৰক অথবা এই ব্যৱহাৰকাৰীৰ আটাইখিনি ডেটা মচক।"</string>
@@ -754,7 +754,7 @@
     <string name="policylab_disableCamera" msgid="5749486347810162018">"কেমেৰাবোৰ অক্ষম কৰক"</string>
     <string name="policydesc_disableCamera" msgid="3204405908799676104">"সকলো ডিভাইচৰ কেমেৰাবোৰ ব্যৱহাৰ কৰাত বাধা দিয়ক।"</string>
     <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"স্ক্ৰীন লকৰ কিছুমান সুবিধা অক্ষম কৰক"</string>
-    <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"স্ক্ৰীণ লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
+    <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"স্ক্ৰীন লকৰ কিছুমান সুবিধা ব্যৱহাৰ হোৱাত বাধা দিয়ক।"</string>
   <string-array name="phoneTypes">
     <item msgid="8996339953292723951">"ঘৰ"</item>
     <item msgid="7740243458912727194">"ম’বাইল"</item>
@@ -1212,7 +1212,7 @@
     <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"প্ৰতিচ্ছবি তোলক"</string>
     <string name="alwaysUse" msgid="3153558199076112903">"এই কার্যৰ বাবে পূর্বনির্ধাৰিত ধৰণে ব্যৱহাৰ কৰক।"</string>
     <string name="use_a_different_app" msgid="4987790276170972776">"এটা পৃথক এপ্ ব্যৱহাৰ কৰক"</string>
-    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
+    <string name="clearDefaultHintMsg" msgid="1325866337702524936">"ছিষ্টেমৰ ছেটিং &gt; এপ্‌ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ডিফ\'ল্ট মচক৷"</string>
     <string name="chooseActivity" msgid="8563390197659779956">"কোনো কার্য বাছনি কৰক"</string>
     <string name="chooseUsbActivity" msgid="2096269989990986612">"ইউএছবি ডিভাইচৰ বাবে এটা এপ্ বাছনি কৰক"</string>
     <string name="noApplications" msgid="1186909265235544019">"কোনো এপে এই কাৰ্য কৰিব নোৱাৰে।"</string>
@@ -1240,7 +1240,7 @@
     <string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক পূৰ্বতে লঞ্চ কৰা হৈছিল৷"</string>
     <string name="screen_compat_mode_scale" msgid="8627359598437527726">"স্কেল"</string>
     <string name="screen_compat_mode_show" msgid="5080361367584709857">"সদায় দেখুৱাওক"</string>
-    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"ছিষ্টেমৰ ছেটিংসমূহ &gt; এপসমূহ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
+    <string name="screen_compat_mode_hint" msgid="4032272159093750908">"ছিষ্টেমৰ ছেটিং &gt; এপ্‌ &gt; ডাউনল’ড কৰা সমল-লৈ গৈ ইয়াক আকৌ সক্ষম কৰক।"</string>
     <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বর্তমানৰ ডিছপ্লে’ৰ আকাৰ ছেটিং ব্যৱহাৰ কৰিব নোৱাৰে আৰু ই সঠিকভাৱে নচলিবও পাৰে।"</string>
     <string name="unsupported_display_size_show" msgid="980129850974919375">"সদায় দেখুৱাওক"</string>
     <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক এটা খাপ নোখোৱা Android OS সংস্কৰণৰ বাবে তৈয়াৰ কৰা হৈছিল, যাৰ ফলত ই অস্বাভাৱিকধৰণে আচৰণ কৰিব পাৰে। এপটোৰ শেহতীয়া সংস্কৰণ উপলব্ধ হ\'ব পাৰে।"</string>
@@ -1337,7 +1337,7 @@
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"পঠিয়াওক"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"বাতিল কৰক"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"মোৰ পচন্দ মনত ৰাখিব"</string>
-    <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"আপুনি ইয়াক পিছত ছেটিং &gt; এপ্‌-ত সলনি কৰিব পাৰে"</string>
+    <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"আপুনি ইয়াক পিছত ছেটিং &gt; এপত সলনি কৰিব পাৰে"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"যিকোনো সময়ত অনুমতি দিয়ক"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"কেতিয়াও অনুমতি নিদিব"</string>
     <string name="sim_removed_title" msgid="5387212933992546283">"ছিম কাৰ্ড আঁতৰোৱা হ’ল"</string>
@@ -1402,7 +1402,7 @@
     <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"অন্য এপৰ ওপৰত দেখুৱায়"</string>
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> অন্য এপসমূহৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
     <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g>এ অইন এপবোৰৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
-    <string name="alert_windows_notification_message" msgid="6538171456970725333">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g>এ এই সুবিধাটো ব্যৱহাৰ কৰাটো নিবিচাৰে তেন্তে টিপি ছেটিংসমূহ খোলক আৰু ইয়াক অফ কৰক।"</string>
+    <string name="alert_windows_notification_message" msgid="6538171456970725333">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g>এ এই সুবিধাটো ব্যৱহাৰ কৰাটো নিবিচাৰে তেন্তে ছেটিং খুলিবলৈ টিপক আৰু ইয়াক অফ কৰক।"</string>
     <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"অফ কৰক"</string>
     <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g> পৰীক্ষা কৰি থকা হৈছে…"</string>
     <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"বৰ্তমানৰ সমলৰ সমীক্ষা কৰি থকা হৈছে"</string>
@@ -1504,7 +1504,7 @@
     <string name="vpn_lockdown_connected" msgid="2853127976590658469">"সদা-সক্ৰিয় ভিপিএন সংযোগ কৰা হ’ল"</string>
     <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ বিচ্ছিন্ন কৰা হৈছে"</string>
     <string name="vpn_lockdown_error" msgid="4453048646854247947">"সদা-সক্ৰিয় ভিপিএনৰ লগত সংযোগ কৰিব পৰা নাই"</string>
-    <string name="vpn_lockdown_config" msgid="8331697329868252169">"নেটৱৰ্ক বা ভিপিএন ছেটিংসমূহ সলনি কৰক"</string>
+    <string name="vpn_lockdown_config" msgid="8331697329868252169">"নেটৱৰ্ক বা VPN ছেটিং সলনি কৰক"</string>
     <string name="upload_file" msgid="8651942222301634271">"ফাইল বাছনি কৰক"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"কোনো ফাইল বাছনি কৰা হোৱা নাই"</string>
     <string name="reset" msgid="3865826612628171429">"ৰিছেট কৰক"</string>
@@ -1692,10 +1692,10 @@
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।"</string>
     <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"সাধ্য সুবিধাসমূহৰ বাবে শ্বৰ্টকাট অন কৰিবনে?"</string>
-    <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিংসমূহ &gt; সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
+    <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিং &gt; সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
     <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
     <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g>ৰ শ্বৰ্টকাট অন কৰিবনে?"</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ &gt; সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিং &gt; সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
     <string name="accessibility_shortcut_on" msgid="5463618449556111344">"অন কৰক"</string>
     <string name="accessibility_shortcut_off" msgid="3651336255403648739">"অন নকৰিব"</string>
     <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"অন কৰা আছে"</string>
@@ -1703,8 +1703,8 @@
     <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>ক আপোনাৰ ডিভাইচটোৰ সম্পূর্ণ নিয়ন্ত্ৰণ দিবনে?"</string>
     <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"যদি আপুনি <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে, তেন্তে আপোনাৰ ডিভাইচটোৱে ডেটা এনক্ৰিপশ্বনৰ গুণগত মান উন্নত কৰিবলৈ স্ক্ৰীন লক ব্যৱহাৰ নকৰে।"</string>
     <string name="accessibility_service_warning_description" msgid="291674995220940133">"আপোনাক সাধ্য সুবিধাৰ প্ৰয়োজনসমূহৰ জৰিয়তে সহায় কৰা এপ্‌সমূহৰ বাবে সম্পূর্ণ নিয়ন্ত্ৰণৰ সুবিধাটো সঠিক যদিও অধিকাংশ এপৰ বাবে এয়া সঠিক নহয়।"</string>
-    <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"চাওক আৰু স্ক্ৰীণ নিয়ন্ত্ৰণ কৰক"</string>
-    <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ই স্ক্ৰীণৰ সকলো সমল পঢ়িব পাৰে আৰু অন্য এপ্‌সমূহৰ ওপৰত সমল প্ৰদর্শন কৰিব পাৰে।"</string>
+    <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"চাওক আৰু স্ক্ৰীন নিয়ন্ত্ৰণ কৰক"</string>
+    <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ই স্ক্ৰীনত থকা আটাইখিনি সমল পঢ়িব পাৰে আৰু অন্য এপ্‌সমূহৰ ওপৰত সমল প্ৰদর্শন কৰিব পাৰে।"</string>
     <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"কার্যসমূহ চাওক আৰু কৰক"</string>
     <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ই আপুনি কোনো এপ্ বা হার্ডৱেৰ ছেন্সৰৰ সৈতে কৰা ভাব-বিনিময় আৰু আপোনাৰ হৈ অন্য কোনো লোকে এপৰ সৈতে কৰা ভাব-বিনিময় ট্ৰেক কৰিব পাৰে।"</string>
     <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"অনুমতি দিয়ক"</string>
@@ -1854,7 +1854,7 @@
       <item quantity="other"> <xliff:g id="COUNT">%d</xliff:g> ছেকেণ্ডত আকৌ চেষ্টা কৰক</item>
     </plurals>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"পিছত আকৌ চেষ্টা কৰক"</string>
-    <string name="immersive_cling_title" msgid="2307034298721541791">"স্ক্ৰীণ পূৰ্ণৰূপত চাই আছে"</string>
+    <string name="immersive_cling_title" msgid="2307034298721541791">"স্ক্ৰীন পূৰ্ণৰূপত চাই আছে"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"বুজি পালোঁ"</string>
     <string name="done_label" msgid="7283767013231718521">"সম্পন্ন কৰা হ’ল"</string>
@@ -2087,7 +2087,7 @@
     <string name="zen_upgrade_notification_title" msgid="8198167698095298717">"অসুবিধা নিদিব সলনি হৈছে"</string>
     <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"কি কি অৱৰোধ কৰা হৈছে জানিবলৈ টিপক।"</string>
     <string name="notification_app_name_system" msgid="3045196791746735601">"ছিষ্টেম"</string>
-    <string name="notification_app_name_settings" msgid="9088548800899952531">"ছেটিংসমূহ"</string>
+    <string name="notification_app_name_settings" msgid="9088548800899952531">"ছেটিং"</string>
     <string name="notification_appops_camera_active" msgid="8177643089272352083">"কেমেৰা"</string>
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"মাইক্ৰ\'ফ\'ন"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে"</string>
@@ -2141,7 +2141,7 @@
     <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"উভতি যাওক"</string>
     <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"শেহতীয়া এপ্‌সমূহ"</string>
     <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"জাননীসমূহ"</string>
-    <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"ক্ষিপ্ৰ ছেটিংসমূহ"</string>
+    <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"ক্ষিপ্ৰ ছেটিং"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"পাৱাৰ ডায়লগ"</string>
     <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"লক স্ক্ৰীন"</string>
     <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"স্ক্ৰীণশ্বট"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a35c39e..7bc5905 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -325,7 +325,7 @@
     <string name="permgrouplab_phone" msgid="570318944091926620">"Telèfon"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"fer i gestionar trucades telefòniques"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensors corporals"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre els signes vitals"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre les constants vitals"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contingut de la finestra"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contingut d\'una finestra amb què estàs interaccionant."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar Exploració tàctil"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c653c31..afc0644 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1058,7 +1058,7 @@
     </plurals>
     <string name="last_month" msgid="1528906781083518683">"El mes pasado"</string>
     <string name="older" msgid="1645159827884647400">"Anterior"</string>
-    <string name="preposition_for_date" msgid="2780767868832729599">"el <xliff:g id="DATE">%s</xliff:g>"</string>
+    <string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="4336835286453822053">"a las <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="3149809685340130039">"en <xliff:g id="YEAR">%s</xliff:g>"</string>
     <string name="day" msgid="8394717255950176156">"día"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 221c20e..d674a5d9 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -601,7 +601,7 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
-    <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible utiliser capteur empreinte digitale. Consultez un fournisseur de services de réparation"</string>
+    <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le capteur d\'empreintes digitales. Consultez un fournisseur de services de réparation"</string>
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 304bd15..6cde847 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -747,10 +747,10 @@
     <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"इस फ़ोन पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string>
     <string name="policylab_setGlobalProxy" msgid="215332221188670221">"डिवाइस वैश्विक प्रॉक्‍सी सेट करें"</string>
     <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"नीति चालू होने के दौरान इस्तेमाल करने के लिए डिवाइस ग्लोबल प्रॉक्‍सी सेट करें. केवल डिवाइस का मालिक ही ग्लोबल प्रॉक्‍सी सेट कर सकता है."</string>
-    <string name="policylab_expirePassword" msgid="6015404400532459169">"स्‍क्रीन लॉक पासवर्ड समाप्‍ति सेट करें"</string>
+    <string name="policylab_expirePassword" msgid="6015404400532459169">"स्‍क्रीन लॉक पासवर्ड के खत्म होने की अवधि सेट करें"</string>
     <string name="policydesc_expirePassword" msgid="9136524319325960675">"यह बदलें कि स्‍क्रीन लॉक पासवर्ड, पिन या पैटर्न को कितने समय में बदला जाना चाहिए."</string>
     <string name="policylab_encryptedStorage" msgid="9012936958126670110">"मेमोरी को सुरक्षित करने का तरीका सेट करें"</string>
-    <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"संग्रहित ऐप्स डेटा को एन्क्रिप्ट किया जाना आवश्‍यक है."</string>
+    <string name="policydesc_encryptedStorage" msgid="1102516950740375617">"स्टोर किए गए ऐप डेटा को एन्क्रिप्ट किया जाना ज़रूरी है."</string>
     <string name="policylab_disableCamera" msgid="5749486347810162018">"कैमरों को अक्षम करें"</string>
     <string name="policydesc_disableCamera" msgid="3204405908799676104">"सभी डिवाइस कैमरों का उपयोग रोकें."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"स्‍क्रीन लॉक की कुछ सुविधाएं बंद करना"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 62639a3..9498980 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -916,7 +916,7 @@
     <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Құпия сөзді <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
     <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"PIN кодын <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате тердіңіз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін қайталаңыз."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп планшет бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін Google есептік жазбаңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін Google аккаунтыңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Бекітпесін ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате салдыңыз. Тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін сізден Google жүйесіне кіріп телефон бекітпесін ашу сұралады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Планшеттің бекітпесін ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате әрекеттендіңіз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін, телефон зауыттың бастапқы параметрлеріне қайта реттеледі және пайдаланушы деректері жоғалады."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Android TV құрылғыңыздың құлпын <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс ашпадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың зауыттық әдепкі параметрлері қайтарылады және одан барлық пайдаланушы деректері өшіп қалады."</string>
@@ -928,7 +928,7 @@
     <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Кескінді ұмытып қалдыңыз ба?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Аккаунттың бекітпесін ашу"</string>
     <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Тым көп кескін әрекеттері"</string>
-    <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ашу үшін Google есептік жазбаңызбен кіріңіз."</string>
+    <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ашу үшін Google аккаунтыңызбен кіріңіз."</string>
     <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Пайдаланушы атауы (эл. пошта)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Құпия сөз"</string>
     <string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Кіру"</string>
@@ -1332,8 +1332,8 @@
     <string name="sms_control_yes" msgid="4858845109269524622">"Рұқсат беру"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"Өшіру"</string>
     <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; мекенжайына хабар жіберуді қалайды."</string>
-    <string name="sms_short_code_details" msgid="2723725738333388351">"Бұл мобильді есептік жазбаңызда "<b>"өзгерістер"</b>" тудыруы мүмкін."</string>
-    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Бұл мобильді есептік жазбаңызда өзгерістерді тудырады."</b></string>
+    <string name="sms_short_code_details" msgid="2723725738333388351">"Бұл мобильді аккаунтыңызда "<b>"өзгерістер"</b>" тудыруы мүмкін."</string>
+    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Бұл мобильді аккаунтыңызда өзгерістерді тудырады."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Жіберу"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Бас тарту"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Менің таңдауым есте сақталсын"</string>
@@ -1477,7 +1477,7 @@
     <string name="ime_action_default" msgid="8265027027659800121">"Орындау"</string>
     <string name="dial_number_using" msgid="6060769078933953531">"Нөмірді\n <xliff:g id="NUMBER">%s</xliff:g> қолданып теріңіз"</string>
     <string name="create_contact_using" msgid="6200708808003692594">"Байланысты\n <xliff:g id="NUMBER">%s</xliff:g> нөмірі арқылы орнату"</string>
-    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Келесі бір немесе бірнеше қолданба қазір және болашақта есептік жазбаңызға қатынасуға рұқсатты сұрап жатыр."</string>
+    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Келесі бір немесе бірнеше қолданба қазір және болашақта аккаунтыңызға қатынасуға рұқсатты сұрап жатыр."</string>
     <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Бұл өтініштің орындалуын қалайсыз ба?"</string>
     <string name="grant_permissions_header_text" msgid="3420736827804657201">"Кіру өтініші"</string>
     <string name="allow" msgid="6195617008611933762">"Рұқсат беру"</string>
@@ -1684,7 +1684,7 @@
     <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Android TV құрылғыңыздың құлпын <xliff:g id="NUMBER">%d</xliff:g> рет дұрыс ашпадыңыз. Енді Android TV құрылғыңыздың зауыттық әдепкі параметрлері қайтарылады."</string>
     <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Телефонды ашуға <xliff:g id="NUMBER">%d</xliff:g> рет қате әрекеттендіңіз. Телефон бастапқы зауыттық параметрлеріне қайта реттеледі."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. After <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін планшетіңізді аккаунт арқылы ашу өтінішін аласыз.\n\n  <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін есептік жазбаңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Құлыпты ашу өрнегін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет дұрыс сызбадыңыз. Енді тағы <xliff:g id="NUMBER_1">%2$d</xliff:g> рет қателессеңіз, Android TV құрылғыңыздың құлпын ашу үшін аккаунтыңызға кіру керек болады.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайталап көріңіз."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңызды аккаунт арқылы ашу өтінішін аласыз. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
     <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Жою"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index bff48b9..a2aa0ce 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -321,7 +321,7 @@
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"piekļūt jūsu fiziskajām aktivitātēm"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"uzņemt attēlus un ierakstīt videoklipus"</string>
-    <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Tuvējās ierīces"</string>
+    <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Tuvumā esošas ierīces"</string>
     <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"tuvumā esošu ierīču meklēšana un savienojuma izveide ar tām"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"Zvanu žurnāli"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"lasīt un rakstīt tālruņa zvanu žurnālu"</string>
@@ -542,7 +542,7 @@
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Ļauj lietotnei piekļūt pārī savienotām Bluetooth ierīcēm."</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"reklamēšana tuvumā esošās Bluetooth ierīcēs"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Atļauj lietotnei veikt reklamēšanu tuvumā esošās Bluetooth ierīcēs"</string>
-    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"novietojuma noteikšana starp tuvu esošām ultraplatjoslas ierīcēm"</string>
+    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"novietojums starp tuvējām ultraplatjoslas ierīcēm"</string>
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Atļaut lietotnei noteikt relatīvo atrašanās vietu starp tuvumā esošām ultraplatjoslas ierīcēm"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informācija par vēlamo NFC maksājumu pakalpojumu"</string>
     <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ļauj lietotnei iegūt informāciju par vēlamo NFC maksājumu pakalpojumu, piemēram, par reģistrētajiem lietojumprogrammu ID un maršruta galamērķi."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2acc13d..a5e2cbc 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -850,7 +850,7 @@
     <string name="orgTypeOther" msgid="5450675258408005553">"മറ്റുള്ളവ"</string>
     <string name="orgTypeCustom" msgid="1126322047677329218">"ഇഷ്‌ടാനുസൃതം"</string>
     <string name="relationTypeCustom" msgid="282938315217441351">"ഇഷ്‌ടാനുസൃതം"</string>
-    <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+    <string name="relationTypeAssistant" msgid="4057605157116589315">"അസിസ്റ്റന്റ്"</string>
     <string name="relationTypeBrother" msgid="7141662427379247820">"സഹോദരന്‍‌"</string>
     <string name="relationTypeChild" msgid="9076258911292693601">"കുട്ടി"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"ഗാര്‍‌ഹിക പങ്കാളി"</string>
@@ -2010,7 +2010,7 @@
     <string name="app_category_image" msgid="7307840291864213007">"ഫോട്ടോകളും ചിത്രങ്ങളും"</string>
     <string name="app_category_social" msgid="2278269325488344054">"സാമൂഹിക ആപ്സുകളും ആശയവിനിമയവും"</string>
     <string name="app_category_news" msgid="1172762719574964544">"വാർത്തകളും മാസികകളും"</string>
-    <string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; Navigation"</string>
+    <string name="app_category_maps" msgid="6395725487922533156">"മാപ്പുകളും നാവിഗേഷനും"</string>
     <string name="app_category_productivity" msgid="1844422703029557883">"ഉല്‍‌പ്പാദനക്ഷമത"</string>
     <string name="app_category_accessibility" msgid="6643521607848547683">"ഉപയോഗസഹായി"</string>
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ഉപകരണ സ്റ്റോറേജ്"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 23c37b7..eeb2b57 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -408,7 +408,7 @@
     <string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे फोन सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर फोनला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
     <string name="permlab_broadcastSticky" msgid="4552241916400572230">"रोचक प्रसारण पाठवा"</string>
     <string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"रोचक प्रसारणे पाठविण्यासाठी अ‍ॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो टॅब्लेटला धीमा किंवा अस्थिर करू शकतो."</string>
-    <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"चिकट प्रसारणे पाठविण्यासाठी ॲपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो तुमच्या Android TV डिव्हाइसला धीमा किंवा अस्थिर करू शकतो."</string>
+    <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"रोचक प्रसारणे पाठविण्यासाठी अ‍ॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो तुमच्या Android TV डिव्हाइसला धिमा किंवा अस्थिर करू शकतो."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"रोचक प्रसारणे पाठविण्यासाठी अ‍ॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
     <string name="permlab_readContacts" msgid="8776395111787429099">"तुमचे संपर्क वाचा"</string>
     <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"तुमच्या टॅबलेटवर स्टोअर केलेल्‍या तुमच्या संपर्कांविषयीचा डेटा वाचण्याची ॲपला अनुमती देते. अ‍ॅप्सना संपर्क तयार केलेल्या तुमच्या टॅबलेटवरील खात्याचा अ‍ॅक्सेसदेखील असेल. यामध्ये तुम्ही इंस्टॉल केलेल्या ॲप्सने तयार केलेल्या खात्यांचा समावेश असू शकतात. ही परवानगी ॲप्सना तुमचा संपर्क डेटा सेव्ह करण्याची अनुमती देते आणि दुर्भावनापूर्ण ॲप्स तुम्हाला न कळवता संपर्क डेटा शेअर करू शकतात."</string>
@@ -985,7 +985,7 @@
     <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"या पेजवर रहा"</string>
     <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nआपल्‍याला खात्री आहे की तुम्ही या पृष्‍ठावरून नेव्‍हिगेट करू इच्‍छिता?"</string>
     <string name="save_password_label" msgid="9161712335355510035">"पुष्टी करा"</string>
-    <string name="double_tap_toast" msgid="7065519579174882778">"टीप: झूम कमी करण्यासाठी आणि वाढवण्यासाठी दोनदा-टॅप करा."</string>
+    <string name="double_tap_toast" msgid="7065519579174882778">"टीप: झूम कमी करण्यासाठी आणि वाढवण्यासाठी दोनदा टॅप करा."</string>
     <string name="autofill_this_form" msgid="3187132440451621492">"स्वयं-भरण"</string>
     <string name="setup_autofill" msgid="5431369130866618567">"स्वयं-भरण सेट करा"</string>
     <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> सह ऑटोफील करा"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 2e4578c..783fabe 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,4 +33,6 @@
     <color name="call_notification_answer_color">#5DBA80</color>
 
     <color name="personal_apps_suspension_notification_color">#8AB4F8</color>
+
+    <color name="overview_background">@color/overview_background_dark</color>
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index cdaf254..da3164a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2039,7 +2039,7 @@
     <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
     <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
-    <string name="autofill_save_no" msgid="9212826374207023544">"Não, obrigado"</string>
+    <string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
     <string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
     <string name="autofill_save_never" msgid="6821841919831402526">"Nunca"</string>
     <string name="autofill_update_yes" msgid="4608662968996874445">"Atualizar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index cdaf254..da3164a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2039,7 +2039,7 @@
     <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
     <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
     <string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
-    <string name="autofill_save_no" msgid="9212826374207023544">"Não, obrigado"</string>
+    <string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
     <string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
     <string name="autofill_save_never" msgid="6821841919831402526">"Nunca"</string>
     <string name="autofill_update_yes" msgid="4608662968996874445">"Atualizar"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 13de370..1baaa1f 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2110,7 +2110,7 @@
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"மொபைலில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"டேப்லெட்டில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
     <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"சாதனத்தில் போதுமான சார்ஜ் உள்ளது. அம்சங்கள் இனி தடையின்றி இயங்கும்."</string>
-    <string name="mime_type_folder" msgid="2203536499348787650">"கோப்புறை"</string>
+    <string name="mime_type_folder" msgid="2203536499348787650">"ஃபோல்டர்"</string>
     <string name="mime_type_apk" msgid="3168784749499623902">"Android ஆப்ஸ்"</string>
     <string name="mime_type_generic" msgid="4606589110116560228">"ஃபைல்"</string>
     <string name="mime_type_generic_ext" msgid="9220220924380909486">"<xliff:g id="EXTENSION">%1$s</xliff:g> ஃபைல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b1af678c..680fbd2 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -79,8 +79,8 @@
     <string name="CLIRPermanent" msgid="166443681876381118">"మీరు కాలర్ ID సెట్టింగ్‌ను మార్చలేరు."</string>
     <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"మొబైల్ డేటా సేవ లేదు"</string>
     <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
-    <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సేవ లేదు"</string>
-    <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సేవ లేదా అత్యవసర కాలింగ్ లేదు"</string>
+    <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సర్వీస్ లేదు"</string>
+    <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సర్వీస్ లేదా ఎమర్జెన్సీ కాలింగ్ లేదు"</string>
     <string name="RestrictedStateContent" msgid="7693575344608618926">"మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేయబడింది"</string>
     <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేశారు"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"మొబైల్ నెట్‌వర్క్ అందుబాటులో లేదు"</string>
@@ -306,7 +306,7 @@
     <string name="permgroupdesc_contacts" msgid="9163927941244182567">"మీ కాంటాక్ట్‌లను యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"లొకేషన్"</string>
     <string name="permgroupdesc_location" msgid="1995955142118450685">"ఈ పరికర లొకేషన్‌ను యాక్సెస్ చేయడానికి"</string>
-    <string name="permgrouplab_calendar" msgid="6426860926123033230">"క్యాలెండర్"</string>
+    <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్‌ను యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్‌లను పంపడం మరియు వీక్షించడం"</string>
@@ -453,9 +453,9 @@
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"భౌతిక కార్యాకలాపాన్ని గుర్తించండి"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ఈ యాప్ మీ భౌతిక కార్యాకలాపాన్ని గుర్తించగలదు."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"చిత్రాలు మరియు వీడియోలు తీయడం"</string>
-    <string name="permdesc_camera" msgid="5240801376168647151">"యాప్ ఉపయోగంలో ఉన్నపుడు కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
+    <string name="permdesc_camera" msgid="5240801376168647151">"యాప్ ఉపయోగంలో ఉన్నపుడు కెమెరాను ఉపయోగించి ఎప్పుడు కావాలంటే అప్పుడు ఈ యాప్ ఫోటోలు తీయగలదు, వీడియోలు రికార్డ్ చేయగలదు."</string>
     <string name="permlab_backgroundCamera" msgid="7549917926079731681">"బ్యాక్‌గ్రౌండ్‌లో ఫోటోలు, వీడియోలను తీయగలదు"</string>
-    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"కెమెరాను ఉపయోగించి ఈ యాప్ ఎప్పుడైనా ఫోటోలను తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు."</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ఈ యాప్, కెమెరాను ఉపయోగించి ఎప్పుడు కావాలంటే అప్పుడు ఫోటోలు తీయగలదు, వీడియోలు రికార్డ్ చేయగలదు."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ఫోటోలు, వీడియోలు తీయడానికి సిస్టమ్ కెమెరాలకు యాప్, లేదా సేవా యాక్సెస్‌ను అనుమతించండి"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ఈ విశేష లేదా సిస్టమ్ యాప్ ఎప్పుడైనా సిస్టమ్ కెమెరాను ఉపయోగించి ఫోటోలు తీయగలదు, వీడియోలను రికార్డ్ చేయగలదు. యాప్‌కు android.permission.CAMERA అనుమతి ఇవ్వడం కూడా అవసరం"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"కెమెరా పరికరాలు తెరుచుకుంటున్నప్పుడు లేదా మూసుకుంటున్నప్పుడు కాల్‌బ్యాక్‌లను స్వీకరించడానికి యాప్‌ను లేదా సర్వీస్‌ను అనుమతించండి."</string>
@@ -817,7 +817,7 @@
     <string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
     <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"కార్యాలయ మొబైల్"</string>
     <string name="phoneTypeWorkPager" msgid="3748332310638505234">"కార్యాలయ పేజర్"</string>
-    <string name="phoneTypeAssistant" msgid="757550783842231039">"అసిస్టెంట్"</string>
+    <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
     <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
     <string name="eventTypeCustom" msgid="3257367158986466481">"అనుకూలం"</string>
     <string name="eventTypeBirthday" msgid="7770026752793912283">"పుట్టినరోజు"</string>
@@ -850,7 +850,7 @@
     <string name="orgTypeOther" msgid="5450675258408005553">"ఇతరం"</string>
     <string name="orgTypeCustom" msgid="1126322047677329218">"అనుకూలం"</string>
     <string name="relationTypeCustom" msgid="282938315217441351">"అనుకూలం"</string>
-    <string name="relationTypeAssistant" msgid="4057605157116589315">"అసిస్టెంట్"</string>
+    <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
     <string name="relationTypeBrother" msgid="7141662427379247820">"సోదరుడు"</string>
     <string name="relationTypeChild" msgid="9076258911292693601">"బిడ్డ"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"జీవిత భాగస్వామి"</string>
@@ -1046,7 +1046,7 @@
     <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string>
     <string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string>
     <string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string>
-    <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ శోధన"</string>
+    <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ సెర్చ్"</string>
     <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలా?"</string>
     <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
     <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
@@ -1877,7 +1877,7 @@
     <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
     <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్‌గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్‌లు, నిర్దిష్ట ఫీచర్‌లు, ఇంకా కొన్ని నెట్‌వర్క్ కనెక్షన్‌లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
     <string name="battery_saver_description" msgid="8518809702138617167">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్‌గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్‌లు, నిర్దిష్ట ఫీచర్‌లు, ఇంకా కొన్ని నెట్‌వర్క్ కనెక్షన్‌లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string>
-    <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్‌గ్రౌండ్‌లో కొన్ని యాప్‌లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్‌, డేటాను యాక్సెస్ చేయగలదు. కానీ త‌క్కువ సార్లు మాత్ర‌మే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string>
+    <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్‌గ్రౌండ్‌లో కొన్ని యాప్‌లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్‌, డేటాను యాక్సెస్ చేయగలదు. కానీ త‌క్కువ సార్లు మాత్ర‌మే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఇమేజ్‌లు ప్రదర్శించబడవు."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్‌ను ఆన్ చేయాలా?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
@@ -2007,7 +2007,7 @@
     <string name="app_category_game" msgid="4534216074910244790">"గేమ్‌లు"</string>
     <string name="app_category_audio" msgid="8296029904794676222">"సంగీతం &amp; ఆడియో"</string>
     <string name="app_category_video" msgid="2590183854839565814">"చలనచిత్రాలు &amp; వీడియో"</string>
-    <string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు &amp; చిత్రాలు"</string>
+    <string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు, ఇమేజ్‌లు"</string>
     <string name="app_category_social" msgid="2278269325488344054">"సామాజికం &amp; కమ్యూనికేషన్"</string>
     <string name="app_category_news" msgid="1172762719574964544">"వార్తలు &amp; వార్తాపత్రికలు"</string>
     <string name="app_category_maps" msgid="6395725487922533156">"Maps &amp; నావిగేషన్"</string>
@@ -2290,7 +2290,7 @@
     <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"సెట్టింగ్‌లలో ఆన్ చేయండి"</string>
     <string name="dismiss_action" msgid="1728820550388704784">"విస్మరించు"</string>
     <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయండి"</string>
-    <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"పరికరం కెమెరాను అన్‌బ్లాక్ చేయండి"</string>
+    <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయండి"</string>
     <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"&lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; యాప్, ఇతర యాప్‌లు, సర్వీస్‌ల కోసం"</string>
     <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"అన్‌బ్లాక్ చేయండి"</string>
     <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b70cee0..85e3110 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1757,7 +1757,7 @@
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Виберіть функції для кнопки спеціальних можливостей"</string>
     <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Виберіть функції для комбінації з клавішами гучності"</string>
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервіс <xliff:g id="SERVICE_NAME">%s</xliff:g> вимкнено"</string>
-    <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Редагувати засоби"</string>
+    <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змінити"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
     <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Вимкнути ярлик"</string>
     <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 50a322f..15ec82c 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1073,7 +1073,7 @@
     <string name="weeks" msgid="3516247214269821391">"tuần"</string>
     <string name="year" msgid="5182610307741238982">"năm"</string>
     <string name="years" msgid="5797714729103773425">"năm"</string>
-    <string name="now_string_shortest" msgid="3684914126941650330">"ngay lúc này"</string>
+    <string name="now_string_shortest" msgid="3684914126941650330">"vừa xong"</string>
     <plurals name="duration_minutes_shortest" formatted="false" msgid="7519574894537185135">
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>ph</item>
       <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>ph</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a5f5051..9d90dd4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6822,9 +6822,8 @@
                  content for the duration of the animation. -->
             <enum name="bottom" value="-1" />
         </attr>
-        <!-- Special background behind animation.  Only for use with window
-             animations.  Can only be a color, and only black.  If 0, the
-             default, there is no background. -->
+        <!-- Special background behind animation. Only for use with task animations.
+             If 0, the default, there is no background. -->
         <attr name="background" />
         <!-- Special option for window animations: if this window is on top
              of a wallpaper, don't animate the wallpaper with it. -->
@@ -7737,7 +7736,7 @@
 
         <!-- Name of a method on the Context used to inflate the menu that will be
              called when the item is clicked.
-             {@deprecated Menu actually traverses the Context hierarchy looking for the 
+             {@deprecated Menu actually traverses the Context hierarchy looking for the
              relevant method, which is fragile (an intermediate ContextWrapper adding a
              same-named method would change behavior) and restricts bytecode optimizers
              such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->
@@ -8371,6 +8370,17 @@
              @hide @SystemApi -->
         <attr name="supportsAmbientMode" format="boolean" />
 
+        <!-- Indicates that this wallpaper service should receive zoom updates when unfolding.
+             When this value is set to true
+             {@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates
+             when folding or unfolding a foldable device. Wallpapers receive zoom updates using
+             {@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and
+             zoom rendering should be handled manually. Default value is true.
+             When set to false wallpapers can implement custom folding/unfolding behavior
+             by listening to {@link android.hardware.Sensor#TYPE_HINGE_ANGLE}.
+             Corresponds to {@link android.app.WallpaperInfo#shouldUseDefaultUnfoldTransition()} -->
+        <attr name="shouldUseDefaultUnfoldTransition" format="boolean" />
+
         <!-- Uri that specifies a settings Slice for this wallpaper. -->
         <attr name="settingsSliceUri" format="string"/>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8b138f0..cbe0eef 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -654,6 +654,11 @@
         -->
     </integer-array>
 
+    <!-- When entering this device state (defined in device_state_configuration.xml),
+         we should wake the device. -1 to disable the feature (do not wake on any device-state
+         transition). -->
+    <integer name="config_deviceStateOnWhichToWakeUp">-1</integer>
+
     <!-- Indicate the display area rect for foldable devices in folded state. -->
     <string name="config_foldedArea"></string>
 
@@ -3219,6 +3224,11 @@
          and one pSIM) -->
     <integer name="config_num_physical_slots">1</integer>
 
+    <!-- When a radio power off request is received, we will delay completing the request until
+         either IMS moves to the deregistered state or the timeout defined by this configuration
+         elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
+    <integer name="config_delay_for_ims_dereg_millis">0</integer>
+
     <!--Thresholds for LTE dbm in status bar-->
     <integer-array translatable="false" name="config_lteDbmThresholds">
         <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
@@ -4596,6 +4606,13 @@
     <!-- Indicates whether device has a power button fingerprint sensor. -->
     <bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
 
+    <!-- When each intermediate UDFPS enroll stage ends, as a fraction of total progress. -->
+    <string-array name="config_udfps_enroll_stage_thresholds" translatable="false">
+        <item>0.25</item>
+        <item>0.5</item>
+        <item>0.75</item>
+    </string-array>
+
     <!-- Messages that should not be shown to the user during face auth enrollment. This should be
          used to hide messages that may be too chatty or messages that the user can't do much about.
          Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
@@ -4844,8 +4861,8 @@
     <item name="config_fixedOrientationLetterboxAspectRatio" format="float" type="dimen">0.0</item>
 
     <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
-         corners of the activity won't be rounded. -->
-    <integer name="config_letterboxActivityCornersRadius">0</integer>
+         min between device bottom corner radii will be used instead. -->
+    <integer name="config_letterboxActivityCornersRadius">-1</integer>
 
     <!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are
         ignored and 0 is used. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b57055c..7d48904 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3221,6 +3221,7 @@
   <eat-comment />
 
   <staging-public-group type="attr" first-id="0x01ff0000">
+    <public name="shouldUseDefaultUnfoldTransition" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01fe0000">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bac9cf2..9553f95 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -966,6 +966,7 @@
     <style name="TextAppearance.Toast">
         <item name="fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="textSize">14sp</item>
+        <item name="lineHeight">20sp</item>
         <item name="textColor">?android:attr/textColorPrimary</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7d2ae7e..fbc629e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -486,6 +486,7 @@
   <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
   <java-symbol type="string" name="config_deviceSpecificAudioService" />
   <java-symbol type="integer" name="config_num_physical_slots" />
+  <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
   <java-symbol type="array" name="config_integrityRuleProviderPackages" />
   <java-symbol type="bool" name="config_useAssistantVolume" />
   <java-symbol type="string" name="config_bandwidthEstimateSource" />
@@ -2612,6 +2613,7 @@
   <java-symbol type="array" name="config_sfps_sensor_props" />
   <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
   <java-symbol type="bool" name="config_is_powerbutton_fps" />
+  <java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
 
   <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
   <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
@@ -3844,6 +3846,7 @@
 
   <!-- For Foldables -->
   <java-symbol type="array" name="config_foldedDeviceStates" />
+  <java-symbol type="integer" name="config_deviceStateOnWhichToWakeUp" />
   <java-symbol type="string" name="config_foldedArea" />
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
   <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
@@ -4417,7 +4420,7 @@
   <java-symbol type="string" name="view_and_control_notification_title" />
   <java-symbol type="string" name="view_and_control_notification_content" />
   <java-symbol type="array" name="config_accessibility_allowed_install_source" />
-  
+
   <!-- Translation -->
   <java-symbol type="string" name="ui_translation_accessibility_translated_text" />
   <java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
@@ -4441,6 +4444,8 @@
 
   <java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
 
+  <java-symbol type="color" name="overview_background"/>
+
   <java-symbol type="string" name="config_secondaryBuiltInDisplayCutout" />
   <java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
   <java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
index ddb6729..4b19391 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureContextTest.java
@@ -39,9 +39,10 @@
     public void testConstructorAdditionalFlags() {
         final ComponentName componentName = new ComponentName("component", "name");
         final IBinder token = new Binder();
+        final IBinder windowToken = new Binder();
         final ContentCaptureContext ctx = new ContentCaptureContext(/* clientContext= */ null,
                 new ActivityId(/* taskId= */ 666, token), componentName, /* displayId= */
-                42, /* flags= */ 1);
+                42, windowToken, /* flags= */ 1);
         final ContentCaptureContext newCtx = new ContentCaptureContext(ctx, /* extraFlags= */ 2);
         assertThat(newCtx.getFlags()).isEqualTo(3);
         assertThat(newCtx.getActivityComponent()).isEqualTo(componentName);
@@ -50,6 +51,7 @@
         assertThat(activityId.getTaskId()).isEqualTo(666);
         assertThat(activityId.getToken()).isEqualTo(token);
         assertThat(newCtx.getDisplayId()).isEqualTo(42);
+        assertThat(newCtx.getWindowToken()).isEqualTo(windowToken);
         assertThat(newCtx.getExtras()).isNull();
         assertThat(newCtx.getLocusId()).isNull();
         assertThat(newCtx.getParentSessionId()).isNull();
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index b7dc1c5..d4799a8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -592,7 +592,7 @@
     public void testUpdateDisplayMeasuredEnergyStatsLocked() {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
         final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-        bi.initMeasuredEnergyStats();
+        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
 
         clocks.realtime = 0;
         int screen = Display.STATE_OFF;
@@ -677,7 +677,7 @@
     public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
         final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-        bi.initMeasuredEnergyStats();
+        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
         bi.setOnBatteryInternal(true);
 
         final int uid1 = 11500;
@@ -691,7 +691,7 @@
     public void testUpdateCustomMeasuredEnergyStatsLocked() {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
         final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-        bi.initMeasuredEnergyStats();
+        bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
 
         final int bucketA = 0; // Custom bucket 0
         final int bucketB = 1; // Custom bucket 1
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index b851f0a..0135fe8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -403,7 +403,7 @@
         assertNotNull(sensor.getSensorBackgroundTime());
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
 
         sensor = uid.getSensorStats().get(SENSOR_ID);
         assertNotNull(sensor);
@@ -413,7 +413,7 @@
         bi.noteStopSensorLocked(UID, SENSOR_ID);
 
         // Now the sensor timer has stopped so this reset should also take out the sensor.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
 
         sensor = uid.getSensorStats().get(SENSOR_ID);
         assertNull(sensor);
@@ -465,7 +465,7 @@
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
         // but still with 0 times.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
         assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
         assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
         assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
@@ -504,7 +504,7 @@
 
         // Reset the stats. Since the sensor is still running, we should still see the timer
         // but with 0 times.
-        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+        bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
         assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
         assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
         assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 0147cdb..74b6dbe 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -18,6 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.BatteryConsumer;
@@ -263,6 +266,39 @@
                 .of(180.0);
     }
 
+    @Test
+    public void testAggregateBatteryStats_incompatibleSnapshot() {
+        Context context = InstrumentationRegistry.getContext();
+        MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+        batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
+
+        BatteryUsageStatsStore batteryUsageStatsStore = mock(BatteryUsageStatsStore.class);
+
+        when(batteryUsageStatsStore.listBatteryUsageStatsTimestamps())
+                .thenReturn(new long[]{1000, 2000});
+
+        when(batteryUsageStatsStore.loadBatteryUsageStats(1000)).thenReturn(
+                new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames())
+                        .setStatsDuration(1234).build());
+
+        // Add a snapshot, with a different set of custom power components.  It should
+        // be skipped by the aggregation.
+        when(batteryUsageStatsStore.loadBatteryUsageStats(2000)).thenReturn(
+                new BatteryUsageStats.Builder(new String[]{"different"})
+                        .setStatsDuration(4321).build());
+
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+                batteryStats, batteryUsageStatsStore);
+
+        BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+                .aggregateSnapshots(0, 3000)
+                .build();
+        final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+        assertThat(stats.getCustomPowerComponentNames())
+                .isEqualTo(batteryStats.getCustomEnergyConsumerNames());
+        assertThat(stats.getStatsDuration()).isEqualTo(1234);
+    }
+
     private static class TestHandler extends Handler {
         TestHandler() {
             super(Looper.getMainLooper());
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 99d576d..cee1a03 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -57,11 +57,10 @@
         this(new MockClocks());
     }
 
-    public void initMeasuredEnergyStats() {
+    public void initMeasuredEnergyStats(String[] customBucketNames) {
         final boolean[] supportedStandardBuckets =
                 new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
         Arrays.fill(supportedStandardBuckets, true);
-        final String[] customBucketNames = {"FOO", "BAR"};
         mGlobalMeasuredEnergyStats =
                 new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
     }
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 42d80e5..8705067 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -48,6 +48,7 @@
         <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.car.permission.CONTROL_APP_BLOCKING"/>
 
         <!-- use for rotary fragment to enable/disable packages related to rotary -->
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 119c9391..791aeb7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -103,12 +103,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
-    "-2006946193": {
-      "message": "setClientVisible: %s clientVisible=%b Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "-2002500255": {
       "message": "Defer removing snapshot surface in %dms",
       "level": "VERBOSE",
@@ -511,6 +505,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-1556507536": {
+      "message": "Passing transform hint %d for window %s%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-1554521902": {
       "message": "showInsets(ime) was requested by different window: %s ",
       "level": "WARN",
@@ -553,6 +553,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "-1501564055": {
+      "message": "Organized TaskFragment is not ready= %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_APP_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/AppTransitionController.java"
+    },
     "-1499134947": {
       "message": "Removing starting %s from %s",
       "level": "VERBOSE",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 884d27f..9feb619 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -827,7 +827,7 @@
             case RAW_SENSOR:
                 return 16;
             case YCBCR_P010:
-                return 20;
+                return 24;
             case RAW_DEPTH10:
             case RAW10:
                 return 10;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
deleted file mode 100644
index ce4e103..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.organizer.EmbeddingExtensionImpl;
-
-/**
- * Provider class that will instantiate the library implementation. It must be included in the
- * vendor library, and the vendor implementation must match the signature of this class.
- */
-public class ExtensionProvider {
-    /**
-     * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
-     * an OEM by overriding this method.
-     */
-    public static ExtensionInterface getExtensionImpl(Context context) {
-        return new SampleExtensionImpl(context);
-    }
-
-    /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */
-    public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl(
-            @NonNull Context context) {
-        return new EmbeddingExtensionImpl();
-    }
-
-    /**
-     * The support library will use this method to check API version compatibility.
-     * @return API version string in MAJOR.MINOR.PATCH-description format.
-     */
-    public static String getApiVersion() {
-        return "1.0.0-settings_sample";
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
deleted file mode 100644
index 6a53efee..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions;
-
-import android.app.Activity;
-
-import androidx.annotation.NonNull;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
- * class for their implementation.
- */
-abstract class StubExtension implements ExtensionInterface {
-
-    private ExtensionCallback mExtensionCallback;
-    private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
-
-    StubExtension() {
-    }
-
-    @Override
-    public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
-        this.mExtensionCallback = extensionCallback;
-    }
-
-    @Override
-    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
-        this.mWindowLayoutChangeListenerActivities.add(activity);
-        this.onListenersChanged();
-    }
-
-    @Override
-    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
-        this.mWindowLayoutChangeListenerActivities.remove(activity);
-        this.onListenersChanged();
-    }
-
-    void updateWindowLayout(@NonNull Activity activity,
-            @NonNull ExtensionWindowLayoutInfo newLayout) {
-        if (this.mExtensionCallback != null) {
-            mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
-        }
-    }
-
-    @NonNull
-    Set<Activity> getActivitiesListeningForLayoutChanges() {
-        return mWindowLayoutChangeListenerActivities;
-    }
-
-    protected boolean hasListeners() {
-        return !mWindowLayoutChangeListenerActivities.isEmpty();
-    }
-
-    protected abstract void onListenersChanged();
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
new file mode 100644
index 0000000..990d7b6
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.app.ActivityThread;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.SplitController;
+import androidx.window.extensions.layout.WindowLayoutComponent;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+
+/**
+ * The reference implementation of {@link WindowExtensions} that implements the initial API version.
+ */
+public class WindowExtensionsImpl implements WindowExtensions {
+
+    private final Object mLock = new Object();
+    private volatile WindowLayoutComponent mWindowLayoutComponent;
+    private volatile SplitController mSplitController;
+
+    @Override
+    public int getVendorApiLevel() {
+        return 1;
+    }
+
+    @Override
+    public boolean isWindowLayoutComponentAvailable() {
+        return true;
+    }
+
+    @Override
+    public WindowLayoutComponent getWindowLayoutComponent() {
+        if (mWindowLayoutComponent == null) {
+            synchronized (mLock) {
+                if (mWindowLayoutComponent == null) {
+                    Context context = ActivityThread.currentApplication();
+                    mWindowLayoutComponent = new WindowLayoutComponentImpl(context);
+                }
+            }
+        }
+        return mWindowLayoutComponent;
+    }
+
+    /**
+     * Returns {@code true} if {@link ActivityEmbeddingComponent} is present on the device,
+     * {@code false} otherwise. If the component is not available the developer will receive a
+     * single callback with empty data or default values where possible.
+     */
+    @Override
+    public boolean isEmbeddingComponentAvailable() {
+        return true;
+    }
+
+    /**
+     * Returns the OEM implementation of {@link ActivityEmbeddingComponent} if it is supported on
+     * the device. The implementation must match the API level reported in
+     * {@link androidx.window.extensions.WindowExtensions}. An
+     * {@link UnsupportedOperationException} will be thrown if the device does not support
+     * Activity Embedding. Use
+     * {@link WindowExtensions#isEmbeddingComponentAvailable()} to determine if
+     * {@link ActivityEmbeddingComponent} is present.
+     * @return the OEM implementation of {@link ActivityEmbeddingComponent}
+     */
+    @NonNull
+    public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
+        if (mSplitController == null) {
+            synchronized (mLock) {
+                if (mSplitController == null) {
+                    mSplitController = new SplitController();
+                }
+            }
+        }
+        return mSplitController;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
new file mode 100644
index 0000000..f9e1f07
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.annotation.NonNull;
+
+/**
+ * Provides the OEM implementation of {@link WindowExtensions}.
+ */
+public class WindowExtensionsProvider {
+
+    private static final WindowExtensions sWindowExtensions = new WindowExtensionsImpl();
+
+    /**
+     * Returns the OEM implementation of {@link WindowExtensions}. This method is implemented in
+     * the library provided on the device and overwrites one in the Jetpack library included in
+     * apps.
+     * @return the OEM implementation of {@link WindowExtensions}
+     */
+    @NonNull
+    public static WindowExtensions getWindowExtensions() {
+        return sWindowExtensions;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 46c8ffe..85ef270 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,7 +36,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitRule;
 
 import java.util.Map;
 import java.util.concurrent.Executor;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
similarity index 93%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index a41557d..06e7d14 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import android.annotation.NonNull;
 import android.app.Activity;
 
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-
 /**
  * Client-side descriptor of a split that holds two containers.
  */
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
similarity index 88%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a783fcd..e1c8b11 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,19 +31,10 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.util.Pair;
 import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentInfo;
 import android.window.WindowContainerTransaction;
 
-import androidx.window.extensions.embedding.ActivityRule;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import androidx.window.extensions.embedding.SplitRule;
-import androidx.window.extensions.embedding.TaskFragment;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -53,7 +44,8 @@
 /**
  * Main controller class that manages split states and presentation.
  */
-public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback {
+public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
+        ActivityEmbeddingComponent {
 
     private final SplitPresenter mPresenter;
 
@@ -64,6 +56,7 @@
 
     // Callback to Jetpack to notify about changes to split states.
     private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
+    private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
 
     public SplitController() {
         mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
@@ -77,6 +70,7 @@
     }
 
     /** Updates the embedding rules applied to future activity launches. */
+    @Override
     public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
         mSplitRules.clear();
         mSplitRules.addAll(rules);
@@ -103,7 +97,8 @@
     /**
      * Registers the split organizer callback to notify about changes to active splits.
      */
-    public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+    @Override
+    public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
         mEmbeddingCallback = callback;
         updateCallbackIfNecessary();
     }
@@ -119,8 +114,8 @@
         container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
         if (container.isFinished()) {
             mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
-            updateCallbackIfNecessary();
         }
+        updateCallbackIfNecessary();
     }
 
     @Override
@@ -139,8 +134,8 @@
             final boolean shouldFinishDependent =
                     !taskFragmentInfo.isTaskClearedForReuse();
             mPresenter.cleanupContainer(container, shouldFinishDependent);
-            updateCallbackIfNecessary();
         }
+        updateCallbackIfNecessary();
     }
 
     @Override
@@ -164,18 +159,23 @@
         }
     }
 
+    void onActivityCreated(@NonNull Activity launchedActivity) {
+        handleActivityCreated(launchedActivity);
+        updateCallbackIfNecessary();
+    }
+
     /**
      * Checks if the activity start should be routed to a particular container. It can create a new
      * container for the activity and a new split container if necessary.
      */
     // TODO(b/190433398): Break down into smaller functions.
-    void onActivityCreated(@NonNull Activity launchedActivity) {
+    void handleActivityCreated(@NonNull Activity launchedActivity) {
         final List<EmbeddingRule> splitRules = getSplitRules();
         final TaskFragmentContainer currentContainer = getContainerWithActivity(
                 launchedActivity.getActivityToken(), launchedActivity);
 
         // Check if the activity is configured to always be expanded.
-        if (shouldExpand(launchedActivity, splitRules)) {
+        if (shouldExpand(launchedActivity, null, splitRules)) {
             if (shouldContainerBeExpanded(currentContainer)) {
                 // Make sure that the existing container is expanded
                 mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
@@ -240,8 +240,6 @@
 
         mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
                 splitPairRule);
-
-        updateCallbackIfNecessary();
     }
 
     private void onActivityConfigurationChanged(@NonNull Activity activity) {
@@ -501,7 +499,7 @@
                 continue;
             }
             SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
-            if (placeholderRule.getActivityPredicate().test(activity)) {
+            if (placeholderRule.matchesActivity(activity)) {
                 return placeholderRule;
             }
         }
@@ -515,8 +513,16 @@
         if (mEmbeddingCallback == null) {
             return;
         }
-        // TODO(b/190433398): Check if something actually changed
-        mEmbeddingCallback.accept(getActiveSplitStates());
+        if (!allActivitiesCreated()) {
+            return;
+        }
+        List<SplitInfo> currentSplitStates = getActiveSplitStates();
+        if (mLastReportedSplitStates.equals(currentSplitStates)) {
+            return;
+        }
+        mLastReportedSplitStates.clear();
+        mLastReportedSplitStates.addAll(currentSplitStates);
+        mEmbeddingCallback.accept(currentSplitStates);
     }
 
     /**
@@ -525,20 +531,46 @@
     private List<SplitInfo> getActiveSplitStates() {
         List<SplitInfo> splitStates = new ArrayList<>();
         for (SplitContainer container : mSplitContainers) {
-            TaskFragment primaryContainer =
-                    new TaskFragment(
+            if (container.getPrimaryContainer().isEmpty()
+                    || container.getSecondaryContainer().isEmpty()) {
+                // Skipping containers that do not have any activities to report.
+                continue;
+            }
+            ActivityStack primaryContainer =
+                    new ActivityStack(
                             container.getPrimaryContainer().collectActivities());
-            TaskFragment secondaryContainer =
-                    new TaskFragment(
+            ActivityStack secondaryContainer =
+                    new ActivityStack(
                             container.getSecondaryContainer().collectActivities());
             SplitInfo splitState = new SplitInfo(primaryContainer,
-                    secondaryContainer, container.getSplitRule().getSplitRatio());
+                    secondaryContainer,
+                    // Splits that are not showing side-by-side are reported as having 0 split
+                    // ratio, since by definition in the API the primary container occupies no
+                    // width of the split when covered by the secondary.
+                    mPresenter.shouldShowSideBySide(container)
+                            ? container.getSplitRule().getSplitRatio()
+                            : 0.0f);
             splitStates.add(splitState);
         }
         return splitStates;
     }
 
     /**
+     * Checks if all activities that are registered with the containers have already appeared in
+     * the client.
+     */
+    private boolean allActivitiesCreated() {
+        for (TaskFragmentContainer container : mContainers) {
+            if (container.getInfo() == null
+                    || container.getInfo().getActivities().size()
+                    != container.collectActivities().size()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns {@code true} if the container is expanded to occupy full task size.
      * Returns {@code false} if the container is included in an active split.
      */
@@ -567,8 +599,7 @@
                 continue;
             }
             SplitPairRule pairRule = (SplitPairRule) rule;
-            if (pairRule.getActivityIntentPredicate().test(
-                    new Pair(primaryActivity, secondaryActivityIntent))) {
+            if (pairRule.matchesActivityIntentPair(primaryActivity, secondaryActivityIntent)) {
                 return pairRule;
             }
         }
@@ -587,10 +618,9 @@
             }
             SplitPairRule pairRule = (SplitPairRule) rule;
             final Intent intent = secondaryActivity.getIntent();
-            if (pairRule.getActivityPairPredicate().test(
-                    new Pair(primaryActivity, secondaryActivity))
-                    && (intent == null || pairRule.getActivityIntentPredicate().test(
-                            new Pair(primaryActivity, intent)))) {
+            if (pairRule.matchesActivityPair(primaryActivity, secondaryActivity)
+                    && (intent == null
+                    || pairRule.matchesActivityIntentPair(primaryActivity, intent))) {
                 return pairRule;
             }
         }
@@ -611,7 +641,7 @@
      * Returns {@code true} if an Activity with the provided component name should always be
      * expanded to occupy full task bounds. Such activity must not be put in a split.
      */
-    private static boolean shouldExpand(@NonNull Activity activity,
+    private static boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent,
             List<EmbeddingRule> splitRules) {
         if (splitRules == null) {
             return false;
@@ -624,7 +654,9 @@
             if (!activityRule.shouldAlwaysExpand()) {
                 continue;
             }
-            if (activityRule.getActivityPredicate().test(activity)) {
+            if (activity != null && activityRule.matchesActivity(activity)) {
+                return true;
+            } else if (intent != null && activityRule.matchesIntent(intent)) {
                 return true;
             }
         }
@@ -678,11 +710,11 @@
 
     /** Executor that posts on the main application thread. */
     private static class MainThreadExecutor implements Executor {
-        private final Handler handler = new Handler(Looper.getMainLooper());
+        private final Handler mHandler = new Handler(Looper.getMainLooper());
 
         @Override
         public void execute(Runnable r) {
-            handler.post(r);
+            mHandler.post(r);
         }
     }
 
@@ -703,13 +735,25 @@
             }
             final Activity launchingActivity = (Activity) who;
 
-            if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
+            if (shouldExpand(null, intent, getSplitRules())) {
+                setLaunchingInExpandedContainer(launchingActivity, options);
+            } else if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
                 setLaunchingInSameContainer(launchingActivity, intent, options);
             }
 
             return super.onStartActivity(who, intent, options);
         }
 
+        private void setLaunchingInExpandedContainer(Activity launchingActivity, Bundle options) {
+            TaskFragmentContainer newContainer = mPresenter.createNewExpandedContainer(
+                    launchingActivity);
+
+            // Amend the request to let the WM know that the activity should be placed in the
+            // dedicated container.
+            options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+                    newContainer.getTaskFragmentToken());
+        }
+
         /**
          * Returns {@code true} if the activity that is going to be started via the
          * {@code intent} should be paired with the {@code launchingActivity} and is set to be
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
similarity index 80%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index ac85ac8..25292b9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -14,16 +14,19 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.LayoutDirection;
+import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowMetrics;
 import android.window.TaskFragmentCreationParams;
@@ -32,8 +35,6 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.window.extensions.embedding.SplitPairRule;
-import androidx.window.extensions.embedding.SplitRule;
 
 import java.util.concurrent.Executor;
 
@@ -42,13 +43,13 @@
  * {@link SplitController}.
  */
 class SplitPresenter extends JetpackTaskFragmentOrganizer {
-    private static final int POSITION_LEFT = 0;
-    private static final int POSITION_RIGHT = 1;
+    private static final int POSITION_START = 0;
+    private static final int POSITION_END = 1;
     private static final int POSITION_FILL = 2;
 
     @IntDef(value = {
-            POSITION_LEFT,
-            POSITION_RIGHT,
+            POSITION_START,
+            POSITION_END,
             POSITION_FILL,
     })
     private @interface Position {}
@@ -96,13 +97,15 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         final Rect parentBounds = getParentContainerBounds(primaryActivity);
-        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+                isLtr(primaryActivity, rule));
         final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
                 primaryActivity, primaryRectBounds, null);
 
         // Create new empty task fragment
-        TaskFragmentContainer secondaryContainer = mController.newContainer(null);
-        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+        final TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
+                rule, isLtr(primaryActivity, rule));
         createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
                 primaryActivity.getActivityToken(), secondaryRectBounds,
                 WINDOWING_MODE_MULTI_WINDOW);
@@ -135,11 +138,13 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         final Rect parentBounds = getParentContainerBounds(primaryActivity);
-        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+                isLtr(primaryActivity, rule));
         final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
                 primaryActivity, primaryRectBounds, null);
 
-        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+                isLtr(primaryActivity, rule));
         final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
                 secondaryActivity, secondaryRectBounds, primaryContainer);
 
@@ -153,6 +158,20 @@
     }
 
     /**
+     * Creates a new expanded container.
+     */
+    TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
+        final TaskFragmentContainer newContainer = mController.newContainer(null);
+
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        createTaskFragment(wct, newContainer.getTaskFragmentToken(),
+                launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW);
+
+        applyTransaction(wct);
+        return newContainer;
+    }
+
+    /**
      * Creates a new container or resizes an existing container for activity to the provided bounds.
      * @param activity The activity to be re-parented to the container if necessary.
      * @param containerToAvoid Re-parent from this container if an activity is already in it.
@@ -197,8 +216,10 @@
     void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
             @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
         final Rect parentBounds = getParentContainerBounds(launchingActivity);
-        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
-        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+                isLtr(launchingActivity, rule));
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+                isLtr(launchingActivity, rule));
 
         TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
                 launchingActivity.getActivityToken());
@@ -231,8 +252,15 @@
         // Getting the parent bounds using the updated container - it will have the recent value.
         final Rect parentBounds = getParentContainerBounds(updatedContainer);
         final SplitRule rule = splitContainer.getSplitRule();
-        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
-        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+        final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+        if (activity == null) {
+            return;
+        }
+        final boolean isLtr = isLtr(activity, rule);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
+                isLtr);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
+                isLtr);
 
         // If the task fragments are not registered yet, the positions will be updated after they
         // are created again.
@@ -283,36 +311,64 @@
         // TODO(b/190433398): Supply correct insets.
         final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
                 new WindowInsets(new Rect()));
-        return rule.getParentWindowMetricsPredicate().test(parentMetrics);
+        return rule.checkParentMetrics(parentMetrics);
     }
 
     @NonNull
     private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
-            @NonNull SplitRule rule) {
+            @NonNull SplitRule rule, boolean isLtr) {
         if (!shouldShowSideBySide(parentBounds, rule)) {
             return new Rect();
         }
 
-        float splitRatio = rule.getSplitRatio();
+        final float splitRatio = rule.getSplitRatio();
+        final float rtlSplitRatio = 1 - splitRatio;
         switch (position) {
-            case POSITION_LEFT:
-                return new Rect(
-                        parentBounds.left,
-                        parentBounds.top,
-                        (int) (parentBounds.left + parentBounds.width() * splitRatio),
-                        parentBounds.bottom);
-            case POSITION_RIGHT:
-                return new Rect(
-                        (int) (parentBounds.left + parentBounds.width() * splitRatio),
-                        parentBounds.top,
-                        parentBounds.right,
-                        parentBounds.bottom);
+            case POSITION_START:
+                return isLtr ? getLeftContainerBounds(parentBounds, splitRatio)
+                        : getRightContainerBounds(parentBounds, rtlSplitRatio);
+            case POSITION_END:
+                return isLtr ? getRightContainerBounds(parentBounds, splitRatio)
+                        : getLeftContainerBounds(parentBounds, rtlSplitRatio);
             case POSITION_FILL:
                 return parentBounds;
         }
         return parentBounds;
     }
 
+    private Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+        return new Rect(
+                parentBounds.left,
+                parentBounds.top,
+                (int) (parentBounds.left + parentBounds.width() * splitRatio),
+                parentBounds.bottom);
+    }
+
+    private Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
+        return new Rect(
+                (int) (parentBounds.left + parentBounds.width() * splitRatio),
+                parentBounds.top,
+                parentBounds.right,
+                parentBounds.bottom);
+    }
+
+    /**
+     * Checks if a split with the provided rule should be displays in left-to-right layout
+     * direction, either always or with the current configuration.
+     */
+    private boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) {
+        switch (rule.getLayoutDirection()) {
+            case LayoutDirection.LOCALE:
+                return context.getResources().getConfiguration().getLayoutDirection()
+                        == View.LAYOUT_DIRECTION_LTR;
+            case LayoutDirection.RTL:
+                return false;
+            case LayoutDirection.LTR:
+            default:
+                return true;
+        }
+    }
+
     @NonNull
     Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
         final Configuration parentConfig = mFragmentParentConfigs.get(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
new file mode 100644
index 0000000..06f6228
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
+ */
+class TaskFragmentAnimationAdapter {
+    private final Animation mAnimation;
+    private final RemoteAnimationTarget mTarget;
+    private final SurfaceControl mLeash;
+    private final boolean mSizeChanged;
+    private final Point mPosition;
+    private final Transformation mTransformation = new Transformation();
+    private final float[] mMatrix = new float[9];
+    private final float[] mVecs = new float[4];
+    private final Rect mRect = new Rect();
+    private boolean mIsFirstFrame = true;
+
+    TaskFragmentAnimationAdapter(@NonNull Animation animation,
+            @NonNull RemoteAnimationTarget target) {
+        this(animation, target, target.leash, false /* sizeChanged */, null /* position */);
+    }
+
+    /**
+     * @param sizeChanged whether the surface size needs to be changed.
+     */
+    TaskFragmentAnimationAdapter(@NonNull Animation animation,
+            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+            boolean sizeChanged, @Nullable Point position) {
+        mAnimation = animation;
+        mTarget = target;
+        mLeash = leash;
+        mSizeChanged = sizeChanged;
+        mPosition = position != null
+                ? position
+                : new Point(target.localBounds.left, target.localBounds.top);
+    }
+
+    /** Called on frame update. */
+    void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+        if (mIsFirstFrame) {
+            t.show(mLeash);
+            mIsFirstFrame = false;
+        }
+
+        currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
+        mAnimation.getTransformation(currentPlayTime, mTransformation);
+        mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
+        t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+        t.setAlpha(mLeash, mTransformation.getAlpha());
+        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+
+        if (mSizeChanged) {
+            // The following applies an inverse scale to the clip-rect so that it crops "after" the
+            // scale instead of before.
+            mVecs[1] = mVecs[2] = 0;
+            mVecs[0] = mVecs[3] = 1;
+            mTransformation.getMatrix().mapVectors(mVecs);
+            mVecs[0] = 1.f / mVecs[0];
+            mVecs[3] = 1.f / mVecs[3];
+            final Rect clipRect = mTransformation.getClipRect();
+            mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+            mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+            mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+            mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+            t.setWindowCrop(mLeash, mRect);
+        }
+    }
+
+    /** Called after animation finished. */
+    void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+        onAnimationUpdate(t, mAnimation.getDuration());
+    }
+
+    long getDurationHint() {
+        return mAnimation.computeDurationHint();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
similarity index 93%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index b85287d..6579766 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
@@ -45,7 +45,7 @@
         }
         final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
         final RemoteAnimationAdapter animationAdapter =
-                new RemoteAnimationAdapter(mRemoteRunner, 0, 0);
+                new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
         definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
         definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
         definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000..3980d07
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+    private static final String TAG = "TaskFragAnimationRunner";
+    private final Handler mHandler = new Handler(Looper.myLooper());
+    private final TaskFragmentAnimationSpec mAnimationSpec;
+
+    TaskFragmentAnimationRunner() {
+        mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
+    }
+
+    @Nullable
+    private Animator mAnimator;
+
+    @Override
+    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] apps,
+            @NonNull RemoteAnimationTarget[] wallpapers,
+            @NonNull RemoteAnimationTarget[] nonApps,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        if (wallpapers.length != 0 || nonApps.length != 0) {
+            throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+                    + "wallpaper or non-app windows.");
+        }
+        if (TaskFragmentAnimationController.DEBUG) {
+            Log.v(TAG, "onAnimationStart transit=" + transit);
+        }
+        mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
+    }
+
+    @Override
+    public void onAnimationCancelled() {
+        if (TaskFragmentAnimationController.DEBUG) {
+            Log.v(TAG, "onAnimationCancelled");
+        }
+        mHandler.post(this::cancelAnimation);
+    }
+
+    /** Creates and starts animation. */
+    private void startAnimation(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        if (mAnimator != null) {
+            Log.w(TAG, "start new animation when the previous one is not finished yet.");
+            mAnimator.cancel();
+        }
+        mAnimator = createAnimator(transit, targets, finishedCallback);
+        mAnimator.start();
+    }
+
+    /** Cancels animation. */
+    private void cancelAnimation() {
+        if (mAnimator == null) {
+            return;
+        }
+        mAnimator.cancel();
+        mAnimator = null;
+    }
+
+    /** Creates the animator given the transition type and windows. */
+    private Animator createAnimator(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        final List<TaskFragmentAnimationAdapter> adapters =
+                createAnimationAdapters(transit, targets);
+        long duration = 0;
+        for (TaskFragmentAnimationAdapter adapter : adapters) {
+            duration = Math.max(duration, adapter.getDurationHint());
+        }
+        final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+        animator.setDuration(duration);
+        animator.addUpdateListener((anim) -> {
+            // Update all adapters in the same transaction.
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (TaskFragmentAnimationAdapter adapter : adapters) {
+                adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
+            }
+            t.apply();
+        });
+        animator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {}
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                for (TaskFragmentAnimationAdapter adapter : adapters) {
+                    adapter.onAnimationEnd(t);
+                }
+                t.apply();
+
+                try {
+                    finishedCallback.onAnimationFinished();
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+                mAnimator = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {}
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {}
+        });
+        return animator;
+    }
+
+    /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
+    private List<TaskFragmentAnimationAdapter> createAnimationAdapters(
+            @WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets) {
+        switch (transit) {
+            case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+                return createOpenAnimationAdapters(targets);
+            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+                return createCloseAnimationAdapters(targets);
+            case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
+                return createChangeAnimationAdapters(targets);
+            default:
+                throw new IllegalArgumentException("Unhandled transit type=" + transit);
+        }
+    }
+
+    private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+        for (RemoteAnimationTarget target : targets) {
+            final Animation animation =
+                    mAnimationSpec.loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
+            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+        }
+        return adapters;
+    }
+
+    private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+        for (RemoteAnimationTarget target : targets) {
+            final Animation animation =
+                    mAnimationSpec.loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
+            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+        }
+        return adapters;
+    }
+
+    private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+        for (RemoteAnimationTarget target : targets) {
+            if (target.startBounds != null) {
+                final Animation[] animations =
+                        mAnimationSpec.createChangeBoundsChangeAnimations(target);
+                // The snapshot surface will always be at (0, 0) of its parent.
+                adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
+                        target.startLeash, false /* sizeChanged */, new Point(0, 0)));
+                // The end surface will have size change for scaling.
+                adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
+                        target.leash, true /* sizeChanged */, null /* position */));
+                continue;
+            }
+
+            final Animation animation;
+            if (target.hasAnimatingParent) {
+                // No-op if it will be covered by the changing parent window.
+                animation = TaskFragmentAnimationSpec.createNoopAnimation(target);
+            } else if (target.mode == MODE_CLOSING) {
+                animation = mAnimationSpec.createChangeBoundsCloseAnimation(target);
+            } else {
+                animation = mAnimationSpec.createChangeBoundsOpenAnimation(target);
+            }
+            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+        }
+        return adapters;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
new file mode 100644
index 0000000..1e64782
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.RemoteAnimationTarget;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+
+/** Animation spec for TaskFragment transition. */
+class TaskFragmentAnimationSpec {
+
+    private static final String TAG = "TaskFragAnimationSpec";
+    private static final int CHANGE_ANIMATION_DURATION = 517;
+    private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
+    private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
+
+    private final Context mContext;
+    private final TransitionAnimation mTransitionAnimation;
+    private final Interpolator mFastOutExtraSlowInInterpolator;
+    private final LinearInterpolator mLinearInterpolator;
+    private float mTransitionAnimationScaleSetting;
+
+    TaskFragmentAnimationSpec(@NonNull Handler handler) {
+        mContext = ActivityThread.currentActivityThread().getApplication();
+        mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
+        // Initialize the AttributeCache for the TransitionAnimation.
+        AttributeCache.init(mContext);
+        mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_extra_slow_in);
+        mLinearInterpolator = new LinearInterpolator();
+
+        // The transition animation should be adjusted based on the developer option.
+        final ContentResolver resolver = mContext.getContentResolver();
+        mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
+                Settings.Global.TRANSITION_ANIMATION_SCALE,
+                mContext.getResources().getFloat(
+                        R.dimen.config_appTransitionAnimationDurationScaleDefault));
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+                new SettingsObserver(handler));
+    }
+
+    /** For target that doesn't need to be animated. */
+    static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) {
+        // Noop but just keep the target showing/hiding.
+        final float alpha = target.mode == MODE_CLOSING ? 0f : 1f;
+        return new AlphaAnimation(alpha, alpha);
+    }
+
+    /** Animation for target that is opening in a change transition. */
+    Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
+        final Rect bounds = target.localBounds;
+        // The target will be animated in from left or right depends on its position.
+        final int startLeft = bounds.left == 0 ? -bounds.width() : bounds.width();
+
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation animation = new TranslateAnimation(startLeft, 0, 0, 0);
+        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+        animation.setDuration(CHANGE_ANIMATION_DURATION);
+        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    /** Animation for target that is closing in a change transition. */
+    Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
+        final Rect bounds = target.localBounds;
+        // The target will be animated out to left or right depends on its position.
+        final int endLeft = bounds.left == 0 ? -bounds.width() : bounds.width();
+
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation animation = new TranslateAnimation(0, endLeft, 0, 0);
+        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+        animation.setDuration(CHANGE_ANIMATION_DURATION);
+        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    /**
+     * Animation for target that is changing (bounds change) in a change transition.
+     * @return the return array always has two elements. The first one is for the start leash, and
+     *         the second one is for the end leash.
+     */
+    Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) {
+        final Rect startBounds = target.startBounds;
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        final Rect endBounds = target.localBounds;
+        float scaleX = ((float) startBounds.width()) / endBounds.width();
+        float scaleY = ((float) startBounds.height()) / endBounds.height();
+        // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
+        // be scaled up with its parent.
+        float startScaleX = 1.f / scaleX;
+        float startScaleY = 1.f / scaleY;
+
+        // The start leash will be fade out.
+        final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
+        final Animation startAlpha = new AlphaAnimation(1f, 0f);
+        startAlpha.setInterpolator(mLinearInterpolator);
+        startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
+        startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
+        startSet.addAnimation(startAlpha);
+        final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
+                startScaleY);
+        startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
+        startScale.setDuration(CHANGE_ANIMATION_DURATION);
+        startSet.addAnimation(startScale);
+        startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
+                endBounds.height());
+        startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+        // The end leash will be moved into the end position while scaling.
+        final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
+        endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+        final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
+        endScale.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(endScale);
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
+                0, 0);
+        endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(endTranslate);
+        // The end leash is resizing, we should update the window crop based on the clip rect.
+        final Rect startClip = new Rect(startBounds);
+        final Rect endClip = new Rect(endBounds);
+        startClip.offsetTo(0, 0);
+        endClip.offsetTo(0, 0);
+        final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+        clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(clipAnim);
+        endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
+                parentBounds.height());
+        endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+        return new Animation[]{startSet, endSet};
+    }
+
+    Animation loadOpenAnimation(boolean isEnter) {
+        // TODO(b/196173550) We need to customize the animation to handle two open window as one.
+        return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
+                ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+                : R.styleable.WindowAnimation_activityOpenExitAnimation);
+    }
+
+    Animation loadCloseAnimation(boolean isEnter) {
+        // TODO(b/196173550) We need to customize the animation to handle two open window as one.
+        return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
+                ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+                : R.styleable.WindowAnimation_activityCloseExitAnimation);
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver(@NonNull Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mTransitionAnimationScaleSetting = Settings.Global.getFloat(
+                    mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE,
+                    mTransitionAnimationScaleSetting);
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
similarity index 98%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 8503b9f..54e44a7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions.organizer;
+package androidx.window.extensions.embedding;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -212,7 +212,9 @@
             @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
         // Finish own activities
         for (Activity activity : collectActivities()) {
-            activity.finish();
+            if (!activity.isFinishing()) {
+                activity.finish();
+            }
         }
 
         if (!shouldFinishDependent) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
similarity index 63%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index a0d5b00..383d91d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.extensions;
+package androidx.window.extensions.layout;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -36,19 +36,27 @@
 import androidx.window.util.PriorityDataProducer;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
 
 /**
- * Reference implementation of androidx.window.extensions OEM interface for use with
+ * Reference implementation of androidx.window.extensions.layout OEM interface for use with
  * WindowManager Jetpack.
  *
  * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
  * production builds since the interface can still change before reaching stable version.
  * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
  */
-class SampleExtensionImpl extends StubExtension {
+public class WindowLayoutComponentImpl implements WindowLayoutComponent {
     private static final String TAG = "SampleExtension";
+    private static WindowLayoutComponent sInstance;
+
+    private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
+            new HashMap<>();
 
     private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
     private final DataProducer<Integer> mDevicePostureProducer;
@@ -56,7 +64,7 @@
     private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
     private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
 
-    SampleExtensionImpl(Context context) {
+    public WindowLayoutComponentImpl(Context context) {
         mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
         mDevicePostureProducer = new PriorityDataProducer<>(List.of(
                 mSettingsDevicePostureProducer,
@@ -73,28 +81,68 @@
         mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
     }
 
+    /**
+     * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
+     * @param activity hosting a {@link android.view.Window}
+     * @param consumer interested in receiving updates to {@link WindowLayoutInfo}
+     */
+    public void addWindowLayoutInfoListener(@NonNull Activity activity,
+            @NonNull Consumer<WindowLayoutInfo> consumer) {
+        mWindowLayoutChangeListeners.put(activity, consumer);
+        updateRegistrations();
+    }
+
+    /**
+     * Removes a listener no longer interested in receiving updates.
+     * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
+     */
+    public void removeWindowLayoutInfoListener(
+            @NonNull Consumer<WindowLayoutInfo> consumer) {
+        mWindowLayoutChangeListeners.values().remove(consumer);
+        updateRegistrations();
+    }
+
+    void updateWindowLayout(@NonNull Activity activity,
+            @NonNull WindowLayoutInfo newLayout) {
+        Consumer<WindowLayoutInfo> consumer = mWindowLayoutChangeListeners.get(activity);
+        if (consumer != null) {
+            consumer.accept(newLayout);
+        }
+    }
+
+    @NonNull
+    Set<Activity> getActivitiesListeningForLayoutChanges() {
+        return mWindowLayoutChangeListeners.keySet();
+    }
+
+    protected boolean hasListeners() {
+        return !mWindowLayoutChangeListeners.isEmpty();
+    }
+
     private int getFeatureState(DisplayFeature feature) {
         Integer featureState = feature.getState();
         Optional<Integer> posture = mDevicePostureProducer.getData();
-        int fallbackPosture = posture.orElse(ExtensionFoldingFeature.STATE_FLAT);
+        int fallbackPosture = posture.orElse(FoldingFeature.STATE_FLAT);
         return featureState == null ? fallbackPosture : featureState;
     }
 
     private void onDisplayFeaturesChanged() {
         for (Activity activity : getActivitiesListeningForLayoutChanges()) {
-            ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+            WindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
             updateWindowLayout(activity, newLayout);
         }
     }
 
     @NonNull
-    private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
-        List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
-        return new ExtensionWindowLayoutInfo(displayFeatures);
+    private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+        List<androidx.window.extensions.layout.DisplayFeature> displayFeatures =
+                getDisplayFeatures(activity);
+        return new WindowLayoutInfo(displayFeatures);
     }
 
-    private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
-        List<ExtensionDisplayFeature> features = new ArrayList<>();
+    private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
+            @NonNull Activity activity) {
+        List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
         int displayId = activity.getDisplay().getDisplayId();
         if (displayId != DEFAULT_DISPLAY) {
             Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -115,15 +163,14 @@
                 rotateRectToDisplayRotation(displayId, featureRect);
                 transformToWindowSpaceRect(activity, featureRect);
 
-                features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+                features.add(new FoldingFeature(featureRect, baseFeature.getType(),
                         getFeatureState(baseFeature)));
             }
         }
         return features;
     }
 
-    @Override
-    protected void onListenersChanged() {
+    private void updateRegistrations() {
         if (hasListeners()) {
             mSettingsDevicePostureProducer.registerObserversIfNeeded();
             mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
deleted file mode 100644
index 9a8961f..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions.organizer;
-
-import androidx.annotation.NonNull;
-import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
-import androidx.window.extensions.embedding.EmbeddingRule;
-import androidx.window.extensions.embedding.SplitInfo;
-
-import java.util.List;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- * Reference implementation of the activity embedding interface defined in WM Jetpack.
- */
-public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent {
-
-    private final SplitController mSplitController;
-
-    public EmbeddingExtensionImpl() {
-        mSplitController = new SplitController();
-    }
-
-    @Override
-    public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
-        mSplitController.setEmbeddingRules(rules);
-    }
-
-    @Override
-    public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
-        mSplitController.setEmbeddingCallback(consumer);
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
deleted file mode 100644
index 9ee60d8..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentAnimationRunner.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.extensions.organizer;
-
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-
-import androidx.annotation.Nullable;
-
-/** To run the TaskFragment animations. */
-class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
-
-    private static final String TAG = "TaskFragAnimationRunner";
-    private final Handler mHandler = new Handler(Looper.myLooper());
-
-    @Nullable
-    private IRemoteAnimationFinishedCallback mFinishedCallback;
-
-    @Override
-    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-            RemoteAnimationTarget[] apps,
-            RemoteAnimationTarget[] wallpapers,
-            RemoteAnimationTarget[] nonApps,
-            IRemoteAnimationFinishedCallback finishedCallback) {
-        if (wallpapers.length != 0 || nonApps.length != 0) {
-            throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
-                    + "wallpaper or non-app windows.");
-        }
-        if (TaskFragmentAnimationController.DEBUG) {
-            Log.v(TAG, "onAnimationStart transit=" + transit);
-        }
-        mHandler.post(() -> startAnimation(apps, finishedCallback));
-    }
-
-    @Override
-    public void onAnimationCancelled() {
-        if (TaskFragmentAnimationController.DEBUG) {
-            Log.v(TAG, "onAnimationCancelled");
-        }
-        mHandler.post(this::onAnimationFinished);
-    }
-
-    private void startAnimation(RemoteAnimationTarget[] targets,
-            IRemoteAnimationFinishedCallback finishedCallback) {
-        // TODO(b/196173550) replace with actual animations
-        mFinishedCallback = finishedCallback;
-        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
-        for (RemoteAnimationTarget target : targets) {
-            if (target.mode == MODE_OPENING) {
-                t.show(target.leash);
-                t.setAlpha(target.leash, 1);
-            }
-            t.setPosition(target.leash, target.localBounds.left, target.localBounds.top);
-        }
-        t.apply();
-        onAnimationFinished();
-    }
-
-    private void onAnimationFinished() {
-        if (mFinishedCallback == null) {
-            return;
-        }
-        try {
-            mFinishedCallback.onAnimationFinished();
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        mFinishedCallback = null;
-    }
-}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 097febf..42e829e 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/color/split_divider_background.xml b/libs/WindowManager/Shell/res/color/split_divider_background.xml
index 84f4fdf..329e5b9b 100644
--- a/libs/WindowManager/Shell/res/color/split_divider_background.xml
+++ b/libs/WindowManager/Shell/res/color/split_divider_background.xml
@@ -15,5 +15,5 @@
   ~ limitations under the License.
   -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+    <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
 </selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
deleted file mode 100644
index 18dc909..0000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/split_divider_corner_size"
-        android:height="@dimen/split_divider_corner_size"
-        android:viewportWidth="42"
-        android:viewportHeight="42">
-
-    <group android:pivotX="21"
-           android:pivotY="21"
-           android:rotation="180">
-        <path
-            android:fillColor="@color/split_divider_background"
-            android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-    </group>
-
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
deleted file mode 100644
index 931cacf..0000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/split_divider_corner_size"
-        android:height="@dimen/split_divider_corner_size"
-        android:viewportWidth="42"
-        android:viewportHeight="42">
-
-    <group android:pivotX="21"
-           android:pivotY="21"
-           android:rotation="-90">
-        <path
-            android:fillColor="@color/split_divider_background"
-            android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-    </group>
-
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
deleted file mode 100644
index 54e4761..0000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="@dimen/split_divider_corner_size"
-        android:height="@dimen/split_divider_corner_size"
-        android:viewportWidth="42"
-        android:viewportHeight="42">
-
-    <group android:pivotX="21"
-           android:pivotY="21"
-           android:rotation="90">
-        <path
-            android:fillColor="@color/split_divider_background"
-            android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-    </group>
-
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index 94182cd..e3be700 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -24,20 +24,20 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <View style="@style/DockedDividerTopLeftRoundCorner"/>
-
         <View
             style="@style/DockedDividerBackground"
             android:id="@+id/docked_divider_background"/>
 
-        <View style="@style/DockedDividerBottomRightRoundCorner"/>
-
         <com.android.wm.shell.common.split.DividerHandleView
             style="@style/DockedDividerHandle"
             android:id="@+id/docked_divider_handle"
             android:contentDescription="@string/accessibility_divider"
             android:background="@null"/>
 
+        <com.android.wm.shell.common.split.DividerRoundedCorner
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
     </FrameLayout>
 
 </com.android.wm.shell.common.split.DividerView>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 1ace3cd..3258385 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -19,10 +19,10 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ কৰক"</string>
     <string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
-    <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিংসমূহ"</string>
+    <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিং"</string>
     <string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
     <string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> চিত্ৰৰ ভিতৰৰ চিত্ৰত আছে"</string>
-    <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিংসমূহ খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
+    <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিং খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
     <string name="pip_play" msgid="3496151081459417097">"প্লে কৰক"</string>
     <string name="pip_pause" msgid="690688849510295232">"পজ কৰক"</string>
     <string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
@@ -31,25 +31,25 @@
     <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
     <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
+    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</string>
     <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
-    <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীণৰ বিভাজক"</string>
-    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+    <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</string>
+    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
     <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
-    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
-    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
+    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"তলৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড ব্যৱহাৰ কৰা"</string>
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"বাহিৰ হ’বলৈ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ ছোৱাইপ কৰক অথবা এপ্‌টোৰ ওপৰত যিকোনো ঠাইত টিপক"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটো আৰম্ভ কৰক"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটোৰ পৰা বাহিৰ হওক"</string>
-    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিংসমূহ"</string>
+    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিং"</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"অভাৰফ্ল’"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ষ্টেকত পুনৰ যোগ দিয়ক"</string>
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
@@ -58,7 +58,7 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string>
-    <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিংসমূহ"</string>
+    <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 6c223f4..170b2db 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -20,5 +20,5 @@
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"চিত্ৰৰ ভিতৰত চিত্ৰ"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
     <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
-    <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
+    <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
index e5707f3..9eddac4 100644
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ b/libs/WindowManager/Shell/res/values-land/styles.xml
@@ -28,20 +28,6 @@
         <item name="android:layout_height">96dp</item>
     </style>
 
-    <style name="DockedDividerTopLeftRoundCorner">
-        <item name="android:layout_gravity">center_horizontal|top</item>
-        <item name="android:background">@drawable/split_rounded_top</item>
-        <item name="android:layout_width">@dimen/split_divider_corner_size</item>
-        <item name="android:layout_height">@dimen/split_divider_corner_size</item>
-    </style>
-
-    <style name="DockedDividerBottomRightRoundCorner">
-        <item name="android:layout_gravity">center_horizontal|bottom</item>
-        <item name="android:background">@drawable/split_rounded_bottom</item>
-        <item name="android:layout_width">@dimen/split_divider_corner_size</item>
-        <item name="android:layout_height">@dimen/split_divider_corner_size</item>
-    </style>
-
     <style name="DockedDividerMinimizedShadow">
         <item name="android:layout_width">8dp</item>
         <item name="android:layout_height">match_parent</item>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 28ff25a..cb6d4de 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -37,20 +37,6 @@
         <item name="android:background">@color/split_divider_background</item>
     </style>
 
-    <style name="DockedDividerTopLeftRoundCorner">
-        <item name="android:layout_gravity">center_vertical|left</item>
-        <item name="android:background">@drawable/split_rounded_left</item>
-        <item name="android:layout_width">@dimen/split_divider_corner_size</item>
-        <item name="android:layout_height">@dimen/split_divider_corner_size</item>
-    </style>
-
-    <style name="DockedDividerBottomRightRoundCorner">
-        <item name="android:layout_gravity">center_vertical|right</item>
-        <item name="android:background">@drawable/split_rounded_right</item>
-        <item name="android:layout_width">@dimen/split_divider_corner_size</item>
-        <item name="android:layout_height">@dimen/split_divider_corner_size</item>
-    </style>
-
     <style name="DockedDividerMinimizedShadow">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">8dp</item>
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 9d0dd3c..d590ab1 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
@@ -968,7 +968,7 @@
             }
         });
         mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
-            if (mExpandedBubble != null) {
+            if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
                 mExpandedBubble.getExpandedView().setTaskViewAlpha(
                         (float) valueAnimator.getAnimatedValue());
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 0a1cd22..74672a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -28,37 +28,40 @@
 import com.android.wm.shell.R
 import com.android.wm.shell.animation.PhysicsAnimator
 import com.android.wm.shell.common.DismissCircleView
+import android.view.WindowInsets
+import android.view.WindowManager
 
 /*
  * View that handles interactions between DismissCircleView and BubbleStackView.
  */
 class DismissView(context: Context) : FrameLayout(context) {
 
-    var circle = DismissCircleView(context).apply {
-        val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
-        val newParams = LayoutParams(targetSize, targetSize)
-        newParams.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
-        setLayoutParams(newParams)
-        setTranslationY(
-            resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height).toFloat())
-    }
-
+    var circle = DismissCircleView(context)
     var isShowing = false
+
     private val animator = PhysicsAnimator.getInstance(circle)
     private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
     private val DISMISS_SCRIM_FADE_MS = 200
+    private var wm: WindowManager =
+            context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
     init {
         setLayoutParams(LayoutParams(
             ViewGroup.LayoutParams.MATCH_PARENT,
             resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
             Gravity.BOTTOM))
-        setPadding(0, 0, 0, resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+        updatePadding()
         setClipToPadding(false)
         setClipChildren(false)
         setVisibility(View.INVISIBLE)
         setBackgroundResource(
             R.drawable.floating_dismiss_gradient_transition)
-        addView(circle)
+
+        val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+        addView(circle, LayoutParams(targetSize, targetSize,
+                Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
+        // start with circle offscreen so it's animated up
+        circle.setTranslationY(resources.getDimensionPixelSize(
+                R.dimen.floating_dismiss_gradient_height).toFloat())
     }
 
     /**
@@ -91,9 +94,21 @@
     }
 
     fun updateResources() {
-        val targetSize: Int = context.resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+        updatePadding()
+        layoutParams.height = resources.getDimensionPixelSize(
+                R.dimen.floating_dismiss_gradient_height)
+
+        val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
         circle.layoutParams.width = targetSize
         circle.layoutParams.height = targetSize
         circle.requestLayout()
     }
+
+    private fun updatePadding() {
+        val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
+        val navInset = insets.getInsetsIgnoringVisibility(
+                WindowInsets.Type.navigationBars())
+        setPadding(0, 0, 0, navInset.bottom +
+                resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
new file mode 100644
index 0000000..364bb65
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.RoundedCorner;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/**
+ * Draws inverted rounded corners beside divider bar to keep splitting tasks cropped with proper
+ * rounded corners.
+ */
+public class DividerRoundedCorner extends View {
+    private final int mDividerWidth;
+    private final Paint mDividerBarBackground;
+    private final Point mStartPos = new Point();
+    private InvertedRoundedCornerDrawInfo mTopLeftCorner;
+    private InvertedRoundedCornerDrawInfo mTopRightCorner;
+    private InvertedRoundedCornerDrawInfo mBottomLeftCorner;
+    private InvertedRoundedCornerDrawInfo mBottomRightCorner;
+
+    public DividerRoundedCorner(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mDividerWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_bar_width);
+        mDividerBarBackground = new Paint();
+        mDividerBarBackground.setColor(
+                getResources().getColor(R.color.split_divider_background, null));
+        mDividerBarBackground.setFlags(Paint.ANTI_ALIAS_FLAG);
+        mDividerBarBackground.setStyle(Paint.Style.FILL);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mTopLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_LEFT);
+        mTopRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_RIGHT);
+        mBottomLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_LEFT);
+        mBottomRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_RIGHT);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.save();
+
+        mTopLeftCorner.calculateStartPos(mStartPos);
+        canvas.translate(mStartPos.x, mStartPos.y);
+        canvas.drawPath(mTopLeftCorner.mPath, mDividerBarBackground);
+
+        canvas.translate(-mStartPos.x, -mStartPos.y);
+        mTopRightCorner.calculateStartPos(mStartPos);
+        canvas.translate(mStartPos.x, mStartPos.y);
+        canvas.drawPath(mTopRightCorner.mPath, mDividerBarBackground);
+
+        canvas.translate(-mStartPos.x, -mStartPos.y);
+        mBottomLeftCorner.calculateStartPos(mStartPos);
+        canvas.translate(mStartPos.x, mStartPos.y);
+        canvas.drawPath(mBottomLeftCorner.mPath, mDividerBarBackground);
+
+        canvas.translate(-mStartPos.x, -mStartPos.y);
+        mBottomRightCorner.calculateStartPos(mStartPos);
+        canvas.translate(mStartPos.x, mStartPos.y);
+        canvas.drawPath(mBottomRightCorner.mPath, mDividerBarBackground);
+
+        canvas.restore();
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    private boolean isLandscape() {
+        return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+    }
+
+    /**
+     * Holds draw information of the inverted rounded corner at a specific position.
+     *
+     * @see {@link com.android.launcher3.taskbar.TaskbarDragLayer}
+     */
+    private class InvertedRoundedCornerDrawInfo {
+        @RoundedCorner.Position
+        private final int mCornerPosition;
+
+        private final int mRadius;
+
+        private final Path mPath = new Path();
+
+        InvertedRoundedCornerDrawInfo(@RoundedCorner.Position int cornerPosition) {
+            mCornerPosition = cornerPosition;
+
+            final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(cornerPosition);
+            mRadius = roundedCorner == null ? 0 : roundedCorner.getRadius();
+
+            // Starts with a filled square, and then subtracting out a circle from the appropriate
+            // corner.
+            final Path square = new Path();
+            square.addRect(0, 0, mRadius, mRadius, Path.Direction.CW);
+            final Path circle = new Path();
+            circle.addCircle(
+                    isLeftCorner() ? mRadius : 0 /* x */,
+                    isTopCorner() ? mRadius : 0 /* y */,
+                    mRadius, Path.Direction.CW);
+            mPath.op(square, circle, Path.Op.DIFFERENCE);
+        }
+
+        private void calculateStartPos(Point outPos) {
+            if (isLandscape()) {
+                // Place left corner at the right side of the divider bar.
+                outPos.x = isLeftCorner()
+                        ? getWidth() / 2 + mDividerWidth / 2
+                        : getWidth() / 2 - mDividerWidth / 2 - mRadius;
+                outPos.y = isTopCorner() ? 0 : getHeight() - mRadius;
+            } else {
+                outPos.x = isLeftCorner() ? 0 : getWidth() - mRadius;
+                // Place top corner at the bottom of the divider bar.
+                outPos.y = isTopCorner()
+                        ? getHeight() / 2 + mDividerWidth / 2
+                        : getHeight() / 2 - mDividerWidth / 2 - mRadius;
+            }
+        }
+
+        private boolean isLeftCorner() {
+            return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_BOTTOM_LEFT;
+        }
+
+        private boolean isTopCorner() {
+            return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_TOP_RIGHT;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f9ba97f..6ea806b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -25,6 +25,7 @@
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.GestureDetector;
+import android.view.InsetsController;
 import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.MotionEvent;
@@ -34,7 +35,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
@@ -51,12 +51,6 @@
     public static final long TOUCH_ANIMATION_DURATION = 150;
     public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
 
-    // TODO(b/191269755): use the value defined in InsetsController.
-    private static final Interpolator RESIZE_INTERPOLATOR = Interpolators.LINEAR;
-
-    // TODO(b/191269755): use the value defined in InsetsController.
-    private static final int ANIMATION_DURATION_RESIZE = 300;
-
     /** The task bar expanded height. Used to determine whether to insets divider bounds or not. */
     private float mExpandedTaskBarHeight;
 
@@ -142,8 +136,8 @@
             if (animate) {
                 ObjectAnimator animator = ObjectAnimator.ofInt(this,
                         DIVIDER_HEIGHT_PROPERTY, mDividerBounds.height(), mTempRect.height());
-                animator.setInterpolator(RESIZE_INTERPOLATOR);
-                animator.setDuration(ANIMATION_DURATION_RESIZE);
+                animator.setInterpolator(InsetsController.RESIZE_INTERPOLATOR);
+                animator.setDuration(InsetsController.ANIMATION_DURATION_RESIZE);
                 animator.start();
             } else {
                 DIVIDER_HEIGHT_PROPERTY.set(this, mTempRect.height());
@@ -226,16 +220,6 @@
     private void setTouching() {
         setSlippery(false);
         mHandle.setTouching(true, true);
-        if (isLandscape()) {
-            mBackground.animate().scaleX(1.4f);
-        } else {
-            mBackground.animate().scaleY(1.4f);
-        }
-        mBackground.animate()
-                .setInterpolator(Interpolators.TOUCH_RESPONSE)
-                .setDuration(TOUCH_ANIMATION_DURATION)
-                .translationZ(mTouchElevation)
-                .start();
         // Lift handle as well so it doesn't get behind the background, even though it doesn't
         // cast shadow.
         mHandle.animate()
@@ -248,13 +232,6 @@
     private void releaseTouching() {
         setSlippery(true);
         mHandle.setTouching(false, true);
-        mBackground.animate()
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
-                .translationZ(0)
-                .scaleX(1f)
-                .scaleY(1f)
-                .start();
         mHandle.animate()
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 27c8d7a..1c308a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -776,26 +776,31 @@
         boolean adjustSurfaceLayoutForIme(SurfaceControl.Transaction t,
                 SurfaceControl dividerLeash, SurfaceControl leash1, SurfaceControl leash2,
                 SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
-            if (mYOffsetForIme == 0) return false;
+            final boolean showDim = mDimValue1 > 0.001f || mDimValue2 > 0.001f;
+            boolean adjusted = false;
+            if (mYOffsetForIme != 0) {
+                if (dividerLeash != null) {
+                    mTempRect.set(mDividerBounds);
+                    mTempRect.offset(0, mYOffsetForIme);
+                    t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+                }
 
-            if (dividerLeash != null) {
-                mTempRect.set(mDividerBounds);
+                mTempRect.set(mBounds1);
                 mTempRect.offset(0, mYOffsetForIme);
-                t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+                t.setPosition(leash1, mTempRect.left, mTempRect.top);
+
+                mTempRect.set(mBounds2);
+                mTempRect.offset(0, mYOffsetForIme);
+                t.setPosition(leash2, mTempRect.left, mTempRect.top);
+                adjusted = true;
             }
 
-            mTempRect.set(mBounds1);
-            mTempRect.offset(0, mYOffsetForIme);
-            t.setPosition(leash1, mTempRect.left, mTempRect.top);
-
-            mTempRect.set(mBounds2);
-            mTempRect.offset(0, mYOffsetForIme);
-            t.setPosition(leash2, mTempRect.left, mTempRect.top);
-
-            t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
-            t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
-
-            return true;
+            if (showDim) {
+                t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
+                t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
+                adjusted = true;
+            }
+            return adjusted;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index ac02075..8e5c5c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -315,13 +315,13 @@
         mOneHandedController = oneHandedController;
         mPipTransitionController = pipTransitionController;
         mTaskStackListener = taskStackListener;
-        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
-                INPUT_CONSUMER_PIP, mainExecutor);
         //TODO: move this to ShellInit when PipController can be injected
         mMainExecutor.execute(this::init);
     }
 
     public void init() {
+        mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_PIP, mMainExecutor);
         mPipTransitionController.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
             mPipBoundsState.setDisplayId(displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 82092ac..0fbdf90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -30,6 +31,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
@@ -93,6 +95,7 @@
     private int mTargetSize;
     private int mDismissAreaHeight;
     private float mMagneticFieldRadiusPercent = 1f;
+    private WindowInsets mWindowInsets;
 
     private SurfaceControl mTaskLeash;
     private boolean mHasDismissTargetSurface;
@@ -123,6 +126,13 @@
                 mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition));
         mTargetViewContainer.setClipChildren(false);
         mTargetViewContainer.addView(mTargetView);
+        mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
+            if (!windowInsets.equals(mWindowInsets)) {
+                mWindowInsets = windowInsets;
+                updateMagneticTargetSize();
+            }
+            return windowInsets;
+        });
 
         mMagnetizedPip = mMotionHelper.getMagnetizedPip();
         mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
@@ -201,10 +211,13 @@
         final Resources res = mContext.getResources();
         mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
         mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+        final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+        final Insets navInset = insets.getInsetsIgnoringVisibility(
+                WindowInsets.Type.navigationBars());
         final FrameLayout.LayoutParams newParams =
                 new FrameLayout.LayoutParams(mTargetSize, mTargetSize);
         newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-        newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+        newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize(
                 R.dimen.floating_dismiss_bottom_margin);
         mTargetView.setLayoutParams(newParams);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 3538fd2..979aa1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.startingsurface;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.graphics.Color.WHITE;
 import static android.graphics.Color.alpha;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -314,6 +315,12 @@
     }
 
     void scheduleRemove(Runnable onRemove) {
+        // Show the latest content as soon as possible for unlocking to home.
+        if (mActivityType == ACTIVITY_TYPE_HOME) {
+            removeImmediately();
+            onRemove.run();
+            return;
+        }
         if (mScheduledRunnable != null) {
             mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
             mScheduledRunnable = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index e6d6028..bde2b5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -71,23 +71,13 @@
                     + " topIsHome:" + topIsHome);
         }
 
-        final int visibleSplashScreenType = legacySplashScreen
-                ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
-                : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-
         if (!topIsHome) {
-            if (!processRunning) {
+            if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
                 return useEmptySplashScreen
                         ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
-                        : visibleSplashScreenType;
-            }
-            if (newTask) {
-                return useEmptySplashScreen
-                        ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
-                        : visibleSplashScreenType;
-            }
-            if (taskSwitch && !activityCreated) {
-                return visibleSplashScreenType;
+                        : legacySplashScreen
+                                ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+                                : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
             }
         }
         if (taskSwitch && allowTaskSnapshot) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4ba6aca..b673d48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -141,8 +141,9 @@
     static boolean isRotationSeamless(@NonNull TransitionInfo info,
             DisplayController displayController) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
-                "Display is rotating, check if it should be seamless.");
+                "Display is changing, check if it should be seamless.");
         boolean checkedDisplayLayout = false;
+        boolean hasTask = false;
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
 
@@ -166,6 +167,7 @@
                     return false;
                 }
             } else if (change.getTaskInfo() != null) {
+                hasTask = true;
                 // We only enable seamless rotation if all the visible task windows requested it.
                 if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
                     ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -209,8 +211,12 @@
             }
         }
 
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Rotation IS seamless.");
-        return true;
+        // ROTATION_ANIMATION_SEAMLESS can only be requested by task.
+        if (hasTask) {
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Rotation IS seamless.");
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -280,7 +286,6 @@
             final TransitionInfo.Change change = info.getChanges().get(i);
 
             if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
-                    && (change.getEndRotation() != change.getStartRotation())
                     && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
                 boolean isSeamless = isRotationSeamless(info, mDisplayController);
                 final int anim = getRotationAnimation(info);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index ac612a7..c798ace 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -71,7 +71,7 @@
     void removeFiltered(RemoteTransition remote) {
         boolean removed = false;
         for (int i = mFilters.size() - 1; i >= 0; --i) {
-            if (mFilters.get(i).second == remote) {
+            if (mFilters.get(i).second.asBinder().equals(remote.asBinder())) {
                 mFilters.remove(i);
                 removed = true;
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index ada2ed2..13c670a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -186,11 +186,11 @@
             t.setAlpha(mBackColorSurface, 1);
             t.show(mBackColorSurface);
 
+            t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
             t.setPosition(mAnimLeash, 0, 0);
             t.setAlpha(mAnimLeash, 1);
             t.show(mAnimLeash);
 
-            t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
             t.setBuffer(mScreenshotLayer, buffer);
             t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
             t.show(mScreenshotLayer);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index b0f0d71..e391713 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -583,6 +583,13 @@
                         .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
                 .build();
         assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays));
+
+        // Not seamless if there is no changed task.
+        final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
+                        .setRotate().build())
+                .build();
+        assertFalse(DefaultTransitionHandler.isRotationSeamless(noTask, displays));
     }
 
     class TransitionInfoBuilder {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 109b535..3544987 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -50,6 +50,7 @@
 bool Properties::skipEmptyFrames = true;
 bool Properties::useBufferAge = true;
 bool Properties::enablePartialUpdates = true;
+bool Properties::enableRenderEffectCache = false;
 
 DebugLevel Properties::debugLevel = kDebugDisabled;
 OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7df6e2c..d224a54 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -224,6 +224,7 @@
     static bool skipEmptyFrames;
     static bool useBufferAge;
     static bool enablePartialUpdates;
+    static bool enableRenderEffectCache;
 
     // TODO: Move somewhere else?
     static constexpr float textGamma = 1.45f;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 7556af9..2c81c97 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -231,14 +231,34 @@
             SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
             SkPaint paint;
             layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
-            const auto snapshotResult = renderNode->updateSnapshotIfRequired(
-                canvas->recordingContext(),
-                layerProperties.getImageFilter(),
-                clipBounds.roundOut()
-            );
-            sk_sp<SkImage> snapshotImage = snapshotResult->snapshot;
-            srcBounds = snapshotResult->outSubset;
-            offset = snapshotResult->outOffset;
+            sk_sp<SkImage> snapshotImage;
+            auto* imageFilter = layerProperties.getImageFilter();
+            auto recordingContext = canvas->recordingContext();
+            // On some GL vendor implementations, caching the result of
+            // getLayerSurface->makeImageSnapshot() causes a call to
+            // Fence::waitForever without a corresponding signal. This would
+            // lead to ANRs throughout the system.
+            // Instead only cache the SkImage created with the SkImageFilter
+            // for supported devices. Otherwise just create a new SkImage with
+            // the corresponding SkImageFilter each time.
+            // See b/193145089 and b/197263715
+            if (!Properties::enableRenderEffectCache) {
+                if (imageFilter) {
+                    auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
+                    snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter,
+                                                                  subset, clipBounds.roundOut(),
+                                                                  &srcBounds, &offset);
+                } else {
+                    snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
+                }
+            } else {
+                const auto snapshotResult = renderNode->updateSnapshotIfRequired(
+                        recordingContext, layerProperties.getImageFilter(), clipBounds.roundOut());
+                snapshotImage = snapshotResult->snapshot;
+                srcBounds = snapshotResult->outSubset;
+                offset = snapshotResult->outOffset;
+            }
+
             const auto dstBounds = SkIRect::MakeXYWH(offset.x(),
                                                      offset.y(),
                                                      srcBounds.width(),
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index a116781..383c79b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -146,6 +146,9 @@
         LOG_ALWAYS_FATAL("Unsupported wide color space.");
     }
     mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension;
+
+    auto* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
+    Properties::enableRenderEffectCache = (strcmp(vendor, "Qualcomm") != 0);
 }
 
 EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bc10401..6d52b66 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -436,4 +436,8 @@
     oneway void setSpatializerGlobalTransform(in float[] transform);
 
     oneway void recenterHeadTracker();
+
+    void setSpatializerParameter(int key, in byte[] value);
+
+    void getSpatializerParameter(int key, inout byte[] value);
 }
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index b062eea..8b1624b 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -129,6 +129,14 @@
      */
     public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
 
+    /**
+     * @hide
+     * Constant indicating the {@code Spatializer} on this device supports the spatialization of
+     * multichannel bed plus objects.
+     * @see #getImmersiveAudioLevel()
+     */
+    public static final int SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS = 2;
+
     /** @hide */
     @IntDef(flag = false, value = {
             HEAD_TRACKING_MODE_UNSUPPORTED,
@@ -792,6 +800,45 @@
         }
     }
 
+    /**
+     * @hide
+     * Sets a parameter on the platform spatializer effect implementation.
+     * This is to be used for vendor-specific configurations of their effect, keys and values are
+     * not reuseable across implementations.
+     * @param key the parameter to change
+     * @param value an array for the value of the parameter to change
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public void setEffectParameter(int key, @NonNull byte[] value) {
+        Objects.requireNonNull(value);
+        try {
+            mAm.getService().setSpatializerParameter(key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling setEffectParameter", e);
+        }
+    }
+
+    /**
+     * @hide
+     * Retrieves a parameter value from the platform spatializer effect implementation.
+     * This is to be used for vendor-specific configurations of their effect, keys and values are
+     * not reuseable across implementations.
+     * @param key the parameter for which the value is queried
+     * @param value a non-empty array to contain the return value. The caller is responsible for
+     *              passing an array of size matching the parameter.
+     */
+    @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public void getEffectParameter(int key, @NonNull byte[] value) {
+        Objects.requireNonNull(value);
+        try {
+            mAm.getService().getSpatializerParameter(key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling getEffectParameter", e);
+        }
+    }
+
     //-----------------------------------------------------------------------------
     // callback helper definitions
 
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 9e2f479..c471ab1 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -24,7 +24,7 @@
     <string name="label_paper_size" msgid="908654383827777759">"Размер бумаги"</string>
     <string name="label_paper_size_summary" msgid="5668204981332138168">"Размер бумаги:"</string>
     <string name="label_color" msgid="1108690305218188969">"Печать"</string>
-    <string name="label_duplex" msgid="5370037254347072243">"Двусторонний"</string>
+    <string name="label_duplex" msgid="5370037254347072243">"С двух сторон"</string>
     <string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
     <string name="label_pages" msgid="7768589729282182230">"Страницы"</string>
     <string name="destination_default_text" msgid="5422708056807065710">"Выберите принтер"</string>
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
index d3f922a..8d95131 100644
--- a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+    <string name="search_menu" msgid="1914043873178389845">"সন্ধান সম্পৰ্কীয় ছেটিং"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index 50bfbe0..89739f6 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -228,7 +228,7 @@
     <item msgid="8612549335720461635">"৪কে. (সুৰক্ষিত)"</item>
     <item msgid="7322156123728520872">"৪কে. (বৰ্ধিত)"</item>
     <item msgid="7735692090314849188">"৪কে. (বৰ্ধিত, সুৰক্ষিত)"</item>
-    <item msgid="7346816300608639624">"৭২০পি., ১০৮০পি. (দ্বৈত স্ক্ৰীণ)"</item>
+    <item msgid="7346816300608639624">"৭২০পি., ১০৮০পি. (দ্বৈত স্ক্ৰীন)"</item>
   </string-array>
   <string-array name="enable_opengl_traces_entries">
     <item msgid="4433736508877934305">"নাই"</item>
@@ -243,7 +243,7 @@
   </string-array>
   <string-array name="track_frame_time_entries">
     <item msgid="634406443901014984">"অফ হৈ আছে"</item>
-    <item msgid="1288760936356000927">"স্ক্ৰীণত দণ্ড হিচাপে"</item>
+    <item msgid="1288760936356000927">"স্ক্ৰীনত দণ্ড হিচাপে"</item>
     <item msgid="5023908510820531131">"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>ত"</item>
   </string-array>
   <string-array name="debug_hw_overdraw_entries">
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 1ae3452..656fe6a 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -155,7 +155,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"ব্যৱহাৰকাৰী: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"কিছুমান ডিফ\'ল্ট ছেট কৰা হৈছে"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"কোনো ডিফ\'ল্ট ছেট কৰা হোৱা নাই"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"পাঠৰ পৰা কথনৰ ছেটিংসমূহ"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"পাঠৰ পৰা কথনৰ ছেটিং"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"পাঠৰ পৰা কথনৰ আউটপুট"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"কথা কোৱাৰ হাৰ"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"পাঠ কথনৰ বেগ"</string>
@@ -177,8 +177,8 @@
     <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g>ক নেটৱৰ্ক সংযোগৰ দৰকাৰ"</string>
     <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> সমৰ্থিত নহয়"</string>
     <string name="tts_status_checking" msgid="8026559918948285013">"পৰীক্ষা কৰি থকা হৈছে…"</string>
-    <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>ৰ বাবে ছেটিংসমূহ"</string>
-    <string name="tts_engine_settings_button" msgid="477155276199968948">"ইঞ্জিনৰ ছেটিংসমূহ লঞ্চ কৰক"</string>
+    <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>ৰ ছেটিং"</string>
+    <string name="tts_engine_settings_button" msgid="477155276199968948">"ইঞ্জিনৰ ছেটিং লঞ্চ কৰক"</string>
     <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"অগ্ৰাধিকাৰপ্ৰাপ্ত ইঞ্জিন"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"সাধাৰণ"</string>
     <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"কথনভংগী তীব্ৰতা ৰিছেট কৰক"</string>
@@ -201,9 +201,9 @@
     <string name="development_settings_enable" msgid="4285094651288242183">"বিকাশকৰ্তা বিষয়ক বিকল্পসমূহ সক্ষম কৰক"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"এপৰ বিকাশৰ বাবে বিকল্পসমূহ ছেট কৰক"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"এইজন ব্যৱহাৰকাৰীৰ বাবে বিকাশকৰ্তাৰ বিকল্পসমূহ উপলব্ধ নহয়"</string>
-    <string name="vpn_settings_not_available" msgid="2894137119965668920">"ভিপিএন ছেটিংসমূহ এই ব্যৱহাৰকাৰীজনৰ বাবে উপলব্ধ নহয়"</string>
-    <string name="tethering_settings_not_available" msgid="266821736434699780">"এই ব্যৱহাৰকাৰীৰ বাবে টেডাৰিং ছেটিংসমূহ উপলব্ধ নহয়"</string>
-    <string name="apn_settings_not_available" msgid="1147111671403342300">"এই ব্যৱহাৰকাৰীৰ বাবে একচেছ পইণ্টৰ নাম ছেটিংসমূহ উপলব্ধ নহয়"</string>
+    <string name="vpn_settings_not_available" msgid="2894137119965668920">"ভিপিএন ছেটিং এই ব্যৱহাৰকাৰীজনৰ বাবে উপলব্ধ নহয়"</string>
+    <string name="tethering_settings_not_available" msgid="266821736434699780">"এই ব্যৱহাৰকাৰীৰ বাবে টেডাৰিং ছেটিং উপলব্ধ নহয়"</string>
+    <string name="apn_settings_not_available" msgid="1147111671403342300">"এই ব্যৱহাৰকাৰীৰ বাবে এক্সেছ পইণ্টৰ নামৰ ছেটিং উপলব্ধ নহয়"</string>
     <string name="enable_adb" msgid="8072776357237289039">"ইউএছবি ডিবাগিং"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"ইউএছবি সংযোগ হৈ থকাৰ অৱস্থাত ডিবাগ ম\'ড"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"ইউএছবি ডিবাগিং অনুমতিসমূহ প্ৰত্যাহাৰ কৰক"</string>
@@ -238,7 +238,7 @@
     <string name="bugreport_in_power" msgid="8664089072534638709">"বাগ ৰিপৰ্টৰ শ্ৱৰ্টকাট"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"পাৱাৰ মেনুত বাগ প্ৰতিবেদন গ্ৰহণ কৰিবলৈ এটা বুটাম দেখুৱাওক"</string>
     <string name="keep_screen_on" msgid="1187161672348797558">"জাগ্ৰত কৰি ৰাখক"</string>
-    <string name="keep_screen_on_summary" msgid="1510731514101925829">"চ্চাৰ্জ হৈ থকাৰ সময়ত স্ক্ৰীণ কেতিয়াও সুপ্ত অৱস্থালৈ নাযায়"</string>
+    <string name="keep_screen_on_summary" msgid="1510731514101925829">"চ্চাৰ্জ হৈ থকাৰ সময়ত স্ক্ৰীন কেতিয়াও সুপ্ত অৱস্থালৈ নাযায়"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"ব্লুটুথ HCI স্নুপ ল’গ সক্ষম কৰক"</string>
     <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"ব্লুটুথ পেকেট সংগ্ৰহ কৰক। (এই ছেটিংটো সলনি কৰাৰ পিছত ব্লুটুথ ট’গল কৰক)"</string>
     <string name="oem_unlock_enable" msgid="5334869171871566731">"ঔইএম আনলক"</string>
@@ -305,7 +305,7 @@
     <string name="adbwifi_warning_title" msgid="727104571653031865">"ৱায়াৰলেচ ডি\'বাগিংৰ অনুমতি দিবনে?"</string>
     <string name="adbwifi_warning_message" msgid="8005936574322702388">"ৱায়াৰলেচ ডি\'বাগিং কেৱল বিকাশৰ উদ্দেশ্যেৰে কৰা হয়। আপোনাৰ কম্পিউটাৰ আৰু আপোনাৰ ডিভাইচৰ মাজত ডেটা প্ৰতিলিপি কৰিবলৈ, কোনো জাননী নিদিয়াকৈয়ে আপোনাৰ ডিভাইচত এপ্‌সমূহ ইনষ্টল কৰিবলৈ আৰু লগ ডেটা পঢ়িবলৈ এইটো ব্যৱহাৰ কৰক।"</string>
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"আপুনি আগতে ইউএছবি ডিবাগিঙৰ বাবে প্ৰৱেশৰ অনুমতি দিয়া সকলো কম্পিউটাৰৰ পৰা সেই অনুমতি প্ৰত্যাহাৰ কৰেনে?"</string>
-    <string name="dev_settings_warning_title" msgid="8251234890169074553">"বিকাশৰ কামৰ বাবে থকা ছেটিংবিলাকক অনুমতি দিবনে?"</string>
+    <string name="dev_settings_warning_title" msgid="8251234890169074553">"বিকাশৰ কামৰ বাবে থকা ছেটিঙৰ অনুমতি দিবনে?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"এই ছেটিংসমূহ বিকাশৰ কামত ব্যৱহাৰ কৰিবলৈ তৈয়াৰ কৰা হৈছে। সেইবিলাকে আপোনাৰ ডিভাইচ আৰু তাত থকা এপ্লিকেশ্বনসমূহক অকামিলা কৰি পেলাব পাৰে আৰু সেইবিলাকৰ কাৰণে এপ্লিকেশ্বনসমূহে অদ্ভুত আচৰণ কৰিব পাৰে।"</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"ইউএছবিৰ যোগেৰে এপৰ সত্যাপন কৰক"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADTৰ যোগেৰে ইনষ্টল কৰা এপসমূহে কিবা ক্ষতিকাৰক আচৰণ কৰিছে নেকি পৰীক্ষা কৰক।"</string>
@@ -331,9 +331,9 @@
     <string name="media_category" msgid="8122076702526144053">"মিডিয়া"</string>
     <string name="debug_monitoring_category" msgid="1597387133765424994">"নিৰীক্ষণ কৰি থকা হৈছে"</string>
     <string name="strict_mode" msgid="889864762140862437">"কঠোৰ ম’ড সক্ষম কৰা হৈছে"</string>
-    <string name="strict_mode_summary" msgid="1838248687233554654">"যেতিয়া এপসমূহে মুখ্য থ্ৰেডত দীঘলীয়া কাৰ্যকলাপ চলাই, তেতিয়া স্ক্ৰীণ ফ্লাশ্ব কৰক"</string>
+    <string name="strict_mode_summary" msgid="1838248687233554654">"যেতিয়া এপ্সমূহে মুখ্য থ্ৰেডত দীঘলীয়া কাৰ্যকলাপ চলাই, তেতিয়া স্ক্ৰীন ফ্লাশ্ব কৰক"</string>
     <string name="pointer_location" msgid="7516929526199520173">"পইণ্টাৰৰ অৱস্থান"</string>
-    <string name="pointer_location_summary" msgid="957120116989798464">"চলিত স্পৰ্শ-বিষয়ক তথ্যসহ স্ক্ৰীণ অভাৰলে\'"</string>
+    <string name="pointer_location_summary" msgid="957120116989798464">"চলিত স্পৰ্শ-বিষয়ক তথ্যসহ স্ক্ৰীন অভাৰলে’"</string>
     <string name="show_touches" msgid="8437666942161289025">"টেপসমূহ দেখুৱাওক"</string>
     <string name="show_touches_summary" msgid="3692861665994502193">"টিপিলে দৃশ্যায়িত ফীডবেক দিয়ক"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"পৃষ্ঠভাগৰ আপডেইট দেখুৱাওক"</string>
@@ -344,7 +344,7 @@
     <string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"হাৰ্ডৱেৰ লেয়াৰ আপডেইট হওতে সিঁহতক সেউজীয়া ৰঙেৰে ফ্লাশ্ব কৰক"</string>
     <string name="debug_hw_overdraw" msgid="8944851091008756796">"GPU অভাৰড্ৰ ডিবাগ কৰক"</string>
     <string name="disable_overlays" msgid="4206590799671557143">"HW অ’ভাৰলে অক্ষম কৰক"</string>
-    <string name="disable_overlays_summary" msgid="1954852414363338166">"স্ক্ৰীণ কম্প’জিট কৰাৰ বাবে সদায় জিপিইউ ব্যৱহাৰ কৰক"</string>
+    <string name="disable_overlays_summary" msgid="1954852414363338166">"স্ক্ৰীন কম্প’জিট কৰাৰ বাবে সদায় জিপিইউ ব্যৱহাৰ কৰক"</string>
     <string name="simulate_color_space" msgid="1206503300335835151">"ৰঙৰ ঠাই ছিমিউলেইট কৰক"</string>
     <string name="enable_opengl_traces_title" msgid="4638773318659125196">"OpenGL ট্ৰেছ সক্ষম কৰক"</string>
     <string name="usb_audio_disable_routing" msgid="3367656923544254975">"ইউএছবি অডিঅ\' ৰাউটিং অক্ষম কৰক"</string>
@@ -352,7 +352,7 @@
     <string name="debug_layout" msgid="1659216803043339741">"লেআউটৰ সময় দেখুৱাওক"</string>
     <string name="debug_layout_summary" msgid="8825829038287321978">"ক্লিপ বাউণ্ড, মাৰ্জিন আদিসমূহ দেখুৱাওক"</string>
     <string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"আৰটিএল চানেকিৰ দিশ বলেৰে সলনি কৰক"</string>
-    <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"সকলো ভাষাৰ বাবে স্ক্ৰীণৰ চানেকিৰ দিশ RTLলৈ বলেৰে সলনি কৰক"</string>
+    <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"সকলো ভাষাৰ বাবে স্ক্ৰীনৰ চানেকিৰ দিশ RTLলৈ বলেৰে সলনি কৰক"</string>
     <string name="window_blurs" msgid="6831008984828425106">"ৱিণ্ড’ স্তৰত অস্পষ্ট কৰাৰ অনুমতি দিয়ক"</string>
     <string name="force_msaa" msgid="4081288296137775550">"বল ৪গুণ MSAA"</string>
     <string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES 2.0 এপত ৪গুণ MSAA সক্ষম কৰক"</string>
@@ -373,7 +373,7 @@
     <string name="show_all_anrs" msgid="9160563836616468726">"নেপথ্য এএনআৰবোৰ দেখুৱাওক"</string>
     <string name="show_all_anrs_summary" msgid="8562788834431971392">"নেপথ্য এপসমূহৰ বাবে এপে সঁহাৰি দিয়া নাই ডায়ল\'গ প্ৰদৰ্শন কৰক"</string>
     <string name="show_notification_channel_warnings" msgid="3448282400127597331">"জাননী চ্চেনেলৰ সকীয়নিসমূহ দেখুৱাওক"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"কোনো এপে বৈধ চ্চেনেল নোহোৱাকৈ কোনো জাননী প\'ষ্ট কৰিলে স্ক্ৰীণত সকীয়নি প্ৰদৰ্শন হয়"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"কোনো এপে বৈধ চ্চেনেল নোহোৱাকৈ কোনো জাননী প\'ষ্ট কৰিলে স্ক্ৰীনত সকীয়নি প্ৰদৰ্শন হয়"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"বাহ্যিক সঞ্চয়াগাৰত এপক বলেৰে অনুমতি দিয়ক"</string>
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"মেনিফেষ্টৰ মান যিয়েই নহওক, বাহ্যিক সঞ্চয়াগাৰত লিখিবলৈ যিকোনো এপক উপযুক্ত কৰি তোলে"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"বলেৰে কাৰ্যকলাপসমূহৰ আকাৰ সলনি কৰিব পৰা কৰক"</string>
@@ -468,7 +468,7 @@
     <string name="external_source_trusted" msgid="1146522036773132905">"অনুমতি দিয়া হৈছে"</string>
     <string name="external_source_untrusted" msgid="5037891688911672227">"অনুমতি দিয়া হোৱা নাই"</string>
     <string name="install_other_apps" msgid="3232595082023199454">"অজ্ঞাত এপ্ ইনষ্টল কৰক"</string>
-    <string name="home" msgid="973834627243661438">"ছেটিংসমূহৰ গৃহপৃষ্ঠা"</string>
+    <string name="home" msgid="973834627243661438">"Settingsৰ গৃহপৃষ্ঠা"</string>
   <string-array name="battery_labels">
     <item msgid="7878690469765357158">"০%"</item>
     <item msgid="8894873528875953317">"৫০%"</item>
@@ -488,7 +488,7 @@
     <string name="retail_demo_reset_title" msgid="1866911701095959800">"পাছৱৰ্ড দৰকাৰী"</string>
     <string name="active_input_method_subtypes" msgid="4232680535471633046">"সক্ৰিয়হৈ থকা ইনপুট পদ্ধতিসমূহ"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"ছিষ্টেমৰ ভাষা ব্যৱহাৰ কৰক"</string>
-    <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>ৰ ছেটিংবিলাক খুলিব পৰা নগ\'ল"</string>
+    <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>ৰ ছেটিং খুলিব পৰা নগ\'ল"</string>
     <string name="ime_security_warning" msgid="6547562217880551450">"এই ইনপুট পদ্ধতিটোৱে আপুনি টাইপ কৰা আপোনাৰ ব্যক্তিগত ডেটা যেনে পাছৱৰ্ডসমূহ আৰু ক্ৰেডিট কাৰ্ডৰ নম্বৰসমূহকে ধৰি সকলো পাঠ সংগ্ৰহ কৰিবলৈ সক্ষম হ\'ব পাৰে। <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> এপটোৰ লগত ই সংলগ্ন। এই ইনপুট পদ্ধতিটো ব্যৱহাৰ কৰেনে?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"টোকা: ৰিবুট কৰাৰ পিছত আপুনি ফ\'নটো আনলক নকৰালৈকে এই এপটো ষ্টাৰ্ট নহ’ব"</string>
     <string name="ims_reg_title" msgid="8197592958123671062">"আইএমএছ পঞ্জীয়ন স্থিতি"</string>
@@ -546,7 +546,7 @@
     <string name="user_add_user_item_title" msgid="2394272381086965029">"ব্যৱহাৰকাৰী"</string>
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"সীমিত প্ৰ\'ফাইল"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"নতুন ব্যৱহাৰকাৰী যোগ কৰিবনে?"</string>
-    <string name="user_add_user_message_long" msgid="1527434966294733380">"আপুনি অতিৰিক্ত ব্য়ৱহাৰকাৰীক যোগ কৰি এই ডিভাইচটো অন্য় ব্য়ক্তিৰ সৈতে শ্বেয়াৰ কৰিব পাৰে। প্ৰতিজন ব্য়ৱহাৰকাৰীৰ বাবে নিজাকৈ ঠাই আছে যাক তেওঁলোকে এপ্, ৱালপেপাৰ আৰু অন্য়ান্য় বস্তুৰ বাবে নিজৰ উপযোগিতা অনুযায়ী ব্য়ৱহাৰ কৰিব পাৰে। ব্য়ৱহাৰকাৰীসকলে সকলোকে প্ৰভাৱান্বিত কৰা ৱাই-ফাইৰ নিচিনা ডিভাইচৰ ছেটিংসমূহ সাল-সলনি কৰিবও পাৰে।\n\nআপুনি যেতিয়া কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰে সেই ব্য়ক্তিজনে নিজেই নিজৰ বাবে ঠাই ছেট আপ কৰিব লাগিব।\n\nসকলো ব্য়ৱহাৰকাৰীএ অন্য় ব্য়ৱহাৰকাৰীৰ বাবে এপসমূহ আপডে’ট কৰিব পাৰে। সাধ্য় সুবিধাসমূহৰ ছেটিং আৰু সেৱাসমূহ নতুন ব্য়ৱহাৰকাৰীলৈ স্থানান্তৰ নহ\'বও পাৰে।"</string>
+    <string name="user_add_user_message_long" msgid="1527434966294733380">"আপুনি অতিৰিক্ত ব্য়ৱহাৰকাৰীক যোগ কৰি এই ডিভাইচটো অন্য় ব্য়ক্তিৰ সৈতে শ্বেয়াৰ কৰিব পাৰে। প্ৰতিজন ব্য়ৱহাৰকাৰীৰ বাবে নিজাকৈ ঠাই আছে যাক তেওঁলোকে এপ্, ৱালপেপাৰ আৰু অন্য়ান্য় বস্তুৰ বাবে নিজৰ উপযোগিতা অনুযায়ী ব্য়ৱহাৰ কৰিব পাৰে। ব্য়ৱহাৰকাৰীসকলে সকলোকে প্ৰভাৱান্বিত কৰা ৱাই-ফাইৰ নিচিনা ডিভাইচৰ ছেটিং সাল-সলনি কৰিবও পাৰে।\n\nআপুনি যেতিয়া কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰে সেই ব্য়ক্তিজনে নিজেই নিজৰ বাবে ঠাই ছেট আপ কৰিব লাগিব।\n\nসকলো ব্য়ৱহাৰকাৰীয়ে অন্য় ব্য়ৱহাৰকাৰীৰ বাবে এপ্‌সমূহ আপডে’ট কৰিব পাৰে। সাধ্য় সুবিধাসমূহৰ ছেটিং আৰু সেৱাসমূহ নতুন ব্য়ৱহাৰকাৰীলৈ স্থানান্তৰ নহ\'বও পাৰে।"</string>
     <string name="user_add_user_message_short" msgid="3295959985795716166">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ ঠাই ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে সকলো ব্যৱহাৰকাৰীৰ বাবে এপ্ আপডেইট কৰিব পাৰে।"</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"ব্যৱহাৰকাৰী এতিয়া ছেট আপ কৰিবনে?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"ডিভাইচটো লৈ নিজৰ ঠাই ছেটআপ কৰিবলৈ নতুন ব্যৱহাৰকাৰী উপলব্ধ থকাটো নিশ্চিত কৰক"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 921caba..108d86d 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -286,7 +286,7 @@
     <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
     <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quand ce mode est activé, l\'adresse MAC de cet appareil peut changer chaque fois qu\'il se connecte à un réseau Wi-Fi où le changement aléatoire d\'adresse MAC est activé"</string>
     <string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
-    <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
+    <string name="wifi_unmetered_label" msgid="6174142840934095093">"Sans compteur"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
     <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Tailles enreg. par tampon journal"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Effacer l\'espace de stockage persistant de l\'enregistreur ?"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index cbf92af..0f381c9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -542,7 +542,7 @@
     <string name="delete_blob_text" msgid="2819192607255625697">"Ортақ деректерді жою"</string>
     <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Осы ортақ деректерді шынымен жойғыңыз келе ме?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"Пайдаланушылардың өздерінің қолданбалары мен мазмұны болады"</string>
-    <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Өз есептік жазбаңыздан қолданбалар мен мазмұнға қол жетімділікті шектеуіңізге болады"</string>
+    <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Өз аккаунтыңыздан қолданбалар мен мазмұнға қол жетімділікті шектеуіңізге болады"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"Пайдаланушы"</string>
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"Шектелген профайл"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"Жаңа пайдаланушы қосылсын ба?"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 801c037..195fc47 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -28,7 +28,7 @@
     <item msgid="2837871868181677206">"IP 주소를 가져오는 중..."</item>
     <item msgid="4613015005934755724">"연결됨"</item>
     <item msgid="3763530049995655072">"일시 정지됨"</item>
-    <item msgid="7852381437933824454">"연결을 끊는 중…"</item>
+    <item msgid="7852381437933824454">"연결 해제 중…"</item>
     <item msgid="5046795712175415059">"연결 끊김"</item>
     <item msgid="2473654476624070462">"실패"</item>
     <item msgid="9146847076036105115">"차단됨"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 97ad0a0..c455c14 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -65,7 +65,7 @@
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"만료됨"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"연결 끊김"</string>
-    <string name="bluetooth_disconnecting" msgid="7638892134401574338">"연결을 끊는 중…"</string>
+    <string name="bluetooth_disconnecting" msgid="7638892134401574338">"연결 해제 중…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"연결 중…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"연결됨<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"페어링 중..."</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 5da56d1..6f8f99b 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -167,8 +167,8 @@
     <string name="tts_default_lang_summary" msgid="9042620014800063470">"టెక్స్ట్‌ను చదివి వినిపించేటప్పుడు, ఒక్కో భాషకు వాడాల్సిన నిర్దిష్ట వాయిస్‌ను సెట్ చేస్తుంది"</string>
     <string name="tts_play_example_title" msgid="1599468547216481684">"ఒక ఉదాహరణ వినండి"</string>
     <string name="tts_play_example_summary" msgid="634044730710636383">"ప్రసంగ సమన్వయం గురించి సంక్షిప్త ప్రదర్శనను ప్లే చేయి"</string>
-    <string name="tts_install_data_title" msgid="1829942496472751703">"వాయిస్ డేటాను ఇన్‌స్టాల్ చేయి"</string>
-    <string name="tts_install_data_summary" msgid="3608874324992243851">"ప్రసంగ సమన్వయం కోసం అవసరమైన వాయిస్ డేటాను ఇన్‌స్టాల్ చేయండి"</string>
+    <string name="tts_install_data_title" msgid="1829942496472751703">"వాయిస్ డేటాను ఇన్‌స్టాల్ చేయండి"</string>
+    <string name="tts_install_data_summary" msgid="3608874324992243851">"స్పీచ్ సమన్వయం కోసం అవసరమైన వాయిస్ డేటాను ఇన్‌స్టాల్ చేయండి"</string>
     <string name="tts_engine_security_warning" msgid="3372432853837988146">"ఈ ప్రసంగ సమన్వయ ఇంజిన్ చదివి వినిపించబడే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ఇంజిన్‌లో అందించబడుతుంది. ఈ ప్రసంగ సమన్వయ ఇంజిన్ యొక్క వినియోగాన్ని ప్రారంభించాలా?"</string>
     <string name="tts_engine_network_required" msgid="8722087649733906851">"వచనం నుండి ప్రసంగం అవుట్‌పుట్ కోసం ఈ భాషకు పని చేస్తున్న నెట్‌వర్క్ కనెక్షన్ కావాలి."</string>
     <string name="tts_default_sample_string" msgid="6388016028292967973">"ఇది ప్రసంగ సమన్వయానికి ఉదాహరణ"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
index bc0c6f3..794b0eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
@@ -15,6 +15,8 @@
  */
 package com.android.settingslib;
 
+import com.android.settingslib.mobile.TelephonyIcons;
+
 import java.text.SimpleDateFormat;
 import java.util.Objects;
 
@@ -40,9 +42,17 @@
         // For logging.
         public final String name;
 
-        public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
-                int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
-                int discContentDesc) {
+        public IconGroup(
+                String name,
+                int[][] sbIcons,
+                int[][] qsIcons,
+                int[] contentDesc,
+                int sbNullState,
+                int qsNullState,
+                int sbDiscState,
+                int qsDiscState,
+                int discContentDesc
+        ) {
             this.name = name;
             this.sbIcons = sbIcons;
             this.qsIcons = qsIcons;
@@ -131,6 +141,19 @@
                     && other.activityOut == activityOut
                     && other.rssi == rssi;
         }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(
+                    connected,
+                    enabled,
+                    level,
+                    inetCondition,
+                    iconGroup,
+                    activityIn,
+                    activityOut,
+                    rssi);
+        }
     }
 
     /**
@@ -139,18 +162,31 @@
     public static class MobileIconGroup extends IconGroup {
         public final int dataContentDescription; // mContentDescriptionDataType
         public final int dataType;
-        public final boolean isWide;
-        public final int qsDataType;
 
-        public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
-                int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
-                int discContentDesc, int dataContentDesc, int dataType, boolean isWide) {
-            super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
-                qsDiscState, discContentDesc);
+        public MobileIconGroup(
+                String name,
+                int[][] sbIcons,
+                int[][] qsIcons,
+                int[] contentDesc,
+                int sbNullState,
+                int qsNullState,
+                int sbDiscState,
+                int qsDiscState,
+                int discContentDesc,
+                int dataContentDesc,
+                int dataType
+        ) {
+            super(name,
+                    sbIcons,
+                    qsIcons,
+                    contentDesc,
+                    sbNullState,
+                    qsNullState,
+                    sbDiscState,
+                    qsDiscState,
+                    discContentDesc);
             this.dataContentDescription = dataContentDesc;
             this.dataType = dataType;
-            this.isWide = isWide;
-            this.qsDataType = dataType; // TODO: remove this field
         }
     }
 
@@ -187,6 +223,27 @@
             defaultDataOff = state.defaultDataOff;
         }
 
+        /** @return true if this state is disabled or not default data */
+        public boolean isDataDisabledOrNotDefault() {
+            return (iconGroup == TelephonyIcons.DATA_DISABLED
+                    || (iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)) && userSetup;
+        }
+
+        /** @return if this state is considered to have inbound activity */
+        public boolean hasActivityIn() {
+            return dataConnected && !carrierNetworkChangeMode && activityIn;
+        }
+
+        /** @return if this state is considered to have outbound activity */
+        public boolean hasActivityOut() {
+            return dataConnected && !carrierNetworkChangeMode && activityOut;
+        }
+
+        /** @return true if this state should show a RAT icon in quick settings */
+        public boolean showQuickSettingsRatIcon() {
+            return dataConnected || isDataDisabledOrNotDefault();
+        }
+
         @Override
         protected void toString(StringBuilder builder) {
             super.toString(builder);
@@ -202,23 +259,40 @@
             builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
                     .append(',');
             builder.append("userSetup=").append(userSetup).append(',');
-            builder.append("defaultDataOff=").append(defaultDataOff);
+            builder.append("defaultDataOff=").append(defaultDataOff).append(',');
+            builder.append("showQuickSettingsRatIcon=").append(showQuickSettingsRatIcon());
         }
 
         @Override
         public boolean equals(Object o) {
             return super.equals(o)
-                && Objects.equals(((MobileState) o).networkName, networkName)
-                && Objects.equals(((MobileState) o).networkNameData, networkNameData)
-                && ((MobileState) o).dataSim == dataSim
-                && ((MobileState) o).dataConnected == dataConnected
-                && ((MobileState) o).isEmergency == isEmergency
-                && ((MobileState) o).airplaneMode == airplaneMode
-                && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
-                && ((MobileState) o).userSetup == userSetup
-                && ((MobileState) o).isDefault == isDefault
-                && ((MobileState) o).roaming == roaming
-                && ((MobileState) o).defaultDataOff == defaultDataOff;
+                    && Objects.equals(((MobileState) o).networkName, networkName)
+                    && Objects.equals(((MobileState) o).networkNameData, networkNameData)
+                    && ((MobileState) o).dataSim == dataSim
+                    && ((MobileState) o).dataConnected == dataConnected
+                    && ((MobileState) o).isEmergency == isEmergency
+                    && ((MobileState) o).airplaneMode == airplaneMode
+                    && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
+                    && ((MobileState) o).userSetup == userSetup
+                    && ((MobileState) o).isDefault == isDefault
+                    && ((MobileState) o).roaming == roaming
+                    && ((MobileState) o).defaultDataOff == defaultDataOff;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(),
+                    networkName,
+                    networkNameData,
+                    dataSim,
+                    dataConnected,
+                    isEmergency,
+                    airplaneMode,
+                    carrierNetworkChangeMode,
+                    userSetup,
+                    isDefault,
+                    roaming,
+                    defaultDataOff);
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index f8565bc..d4e58f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -50,178 +50,194 @@
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.carrier_network_change_mode,
-            0,
-            false);
+            0
+    );
 
     public static final MobileIconGroup THREE_G = new MobileIconGroup(
             "3G",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_3g,
-            TelephonyIcons.ICON_3G,
-            true);
+            TelephonyIcons.ICON_3G
+    );
 
     public static final MobileIconGroup WFC = new MobileIconGroup(
             "WFC",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
-            0, 0, false);
+            0,
+            0);
 
     public static final MobileIconGroup UNKNOWN = new MobileIconGroup(
             "Unknown",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
-            0, 0, false);
+            0,
+            0);
 
     public static final MobileIconGroup E = new MobileIconGroup(
             "E",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_edge,
-            TelephonyIcons.ICON_E,
-            false);
+            TelephonyIcons.ICON_E
+    );
 
     public static final MobileIconGroup ONE_X = new MobileIconGroup(
             "1X",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_cdma,
-            TelephonyIcons.ICON_1X,
-            true);
+            TelephonyIcons.ICON_1X
+    );
 
     public static final MobileIconGroup G = new MobileIconGroup(
             "G",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_gprs,
-            TelephonyIcons.ICON_G,
-            false);
+            TelephonyIcons.ICON_G
+    );
 
     public static final MobileIconGroup H = new MobileIconGroup(
             "H",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_3_5g,
-            TelephonyIcons.ICON_H,
-            false);
+            TelephonyIcons.ICON_H
+    );
 
     public static final MobileIconGroup H_PLUS = new MobileIconGroup(
             "H+",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_3_5g_plus,
-            TelephonyIcons.ICON_H_PLUS,
-            false);
+            TelephonyIcons.ICON_H_PLUS
+    );
 
     public static final MobileIconGroup FOUR_G = new MobileIconGroup(
             "4G",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_4g,
-            TelephonyIcons.ICON_4G,
-            true);
+            TelephonyIcons.ICON_4G
+    );
 
     public static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
             "4G+",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_4g_plus,
-            TelephonyIcons.ICON_4G_PLUS,
-            true);
+            TelephonyIcons.ICON_4G_PLUS
+    );
 
     public static final MobileIconGroup LTE = new MobileIconGroup(
             "LTE",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_lte,
-            TelephonyIcons.ICON_LTE,
-            true);
+            TelephonyIcons.ICON_LTE
+    );
 
     public static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
             "LTE+",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_lte_plus,
-            TelephonyIcons.ICON_LTE_PLUS,
-            true);
+            TelephonyIcons.ICON_LTE_PLUS
+    );
 
     public static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup(
             "5Ge",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_5ge_html,
-            TelephonyIcons.ICON_5G_E,
-            true);
+            TelephonyIcons.ICON_5G_E
+    );
 
     public static final MobileIconGroup NR_5G = new MobileIconGroup(
             "5G",
@@ -234,8 +250,8 @@
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_5g,
-            TelephonyIcons.ICON_5G,
-            true);
+            TelephonyIcons.ICON_5G
+    );
 
     public static final MobileIconGroup NR_5G_PLUS = new MobileIconGroup(
             "5G_PLUS",
@@ -248,34 +264,36 @@
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_5g_plus,
-            TelephonyIcons.ICON_5G_PLUS,
-            true);
+            TelephonyIcons.ICON_5G_PLUS
+    );
 
     public static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
             "DataDisabled",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.cell_data_off_content_description,
-            0,
-            false);
+            0
+    );
 
     public static final MobileIconGroup NOT_DEFAULT_DATA = new MobileIconGroup(
             "NotDefaultData",
             null,
             null,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
-            0, 0,
+            0,
+            0,
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.not_default_data_content_description,
-            0,
-            false);
+            0
+    );
 
     public static final MobileIconGroup CARRIER_MERGED_WIFI = new MobileIconGroup(
             "CWF",
@@ -288,8 +306,8 @@
             /* qsDiscState= */ 0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
             R.string.data_connection_carrier_wifi,
-            TelephonyIcons.ICON_CWF,
-            /* isWide= */ true);
+            TelephonyIcons.ICON_CWF
+    );
 
     // When adding a new MobileIconGround, check if the dataContentDescription has to be filtered
     // in QSCarrier#hasValidTypeContentDescription
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index b65637f..5ee919b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.icu.text.MeasureFormat;
 import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.MessageFormat;
 import android.icu.text.RelativeDateTimeFormatter;
 import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
 import android.icu.util.Measure;
@@ -31,7 +32,9 @@
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Locale;
+import java.util.Map;
 
 /** Utility class for generally useful string methods **/
 public class StringUtil {
@@ -183,4 +186,37 @@
         return formatRelativeTime(context, millis, withSeconds,
                 RelativeDateTimeFormatter.Style.LONG);
     }
+
+    /**
+     * Get ICU plural string without additional arguments
+     *
+     * @param context Context used to get the string
+     * @param count The number used to get the correct string for the current language's plural
+     *              rules.
+     * @param resId Resource id of the string
+     *
+     * @return Formatted plural string
+     */
+    public static String getIcuPluralsString(Context context, int count, int resId) {
+        MessageFormat msgFormat = new MessageFormat(context.getResources().getString(resId),
+                Locale.getDefault());
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("count", count);
+        return msgFormat.format(arguments);
+    }
+
+    /**
+     * Get ICU plural string with additional arguments
+     *
+     * @param context Context used to get the string
+     * @param args String arguments
+     * @param resId Resource id of the string
+     *
+     * @return Formatted plural string
+     */
+    public static String getIcuPluralsString(Context context, Map<String, Object> args, int resId) {
+        MessageFormat msgFormat = new MessageFormat(context.getResources().getString(resId),
+                Locale.getDefault());
+        return msgFormat.format(args);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
new file mode 100644
index 0000000..92a32bc
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.android.settingslib.mobile.TelephonyIcons;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class MobileStateTest {
+
+    private SignalIcon.MobileState mState = new SignalIcon.MobileState();
+
+    @Before
+    public void setUp() {
+    }
+
+    @Test
+    public void testIsDataDisabledOrNotDefault_dataDisabled() {
+        mState.iconGroup = TelephonyIcons.DATA_DISABLED;
+        mState.userSetup = true;
+
+        assertTrue(mState.isDataDisabledOrNotDefault());
+    }
+
+    @Test
+    public void testIsDataDisabledOrNotDefault_notDefaultData() {
+        mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
+        mState.userSetup = true;
+
+        assertTrue(mState.isDataDisabledOrNotDefault());
+    }
+
+    @Test
+    public void testIsDataDisabledOrNotDefault_notDisabled() {
+        mState.iconGroup = TelephonyIcons.G;
+        mState.userSetup = true;
+
+        assertFalse(mState.isDataDisabledOrNotDefault());
+    }
+
+    @Test
+    public void testHasActivityIn_noData_noActivity() {
+        mState.dataConnected = false;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityIn = false;
+
+        assertFalse(mState.hasActivityIn());
+    }
+
+    @Test
+    public void testHasActivityIn_noData_activityIn() {
+        mState.dataConnected = false;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityIn = true;
+
+        assertFalse(mState.hasActivityIn());
+    }
+
+    @Test
+    public void testHasActivityIn_dataConnected_activityIn() {
+        mState.dataConnected = true;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityIn = true;
+
+        assertTrue(mState.hasActivityIn());
+    }
+
+    @Test
+    public void testHasActivityIn_carrierNetworkChange() {
+        mState.dataConnected = true;
+        mState.carrierNetworkChangeMode = true;
+        mState.activityIn = true;
+
+        assertFalse(mState.hasActivityIn());
+    }
+
+    @Test
+    public void testHasActivityOut_noData_noActivity() {
+        mState.dataConnected = false;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityOut = false;
+
+        assertFalse(mState.hasActivityOut());
+    }
+
+    @Test
+    public void testHasActivityOut_noData_activityOut() {
+        mState.dataConnected = false;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityOut = true;
+
+        assertFalse(mState.hasActivityOut());
+    }
+
+    @Test
+    public void testHasActivityOut_dataConnected_activityOut() {
+        mState.dataConnected = true;
+        mState.carrierNetworkChangeMode = false;
+        mState.activityOut = true;
+
+        assertTrue(mState.hasActivityOut());
+    }
+
+    @Test
+    public void testHasActivityOut_carrierNetworkChange() {
+        mState.dataConnected = true;
+        mState.carrierNetworkChangeMode = true;
+        mState.activityOut = true;
+
+        assertFalse(mState.hasActivityOut());
+    }
+}
diff --git a/packages/SettingsProvider/res/values-as/strings.xml b/packages/SettingsProvider/res/values-as/strings.xml
index 89b7c1e..ead9f4d 100644
--- a/packages/SettingsProvider/res/values-as/strings.xml
+++ b/packages/SettingsProvider/res/values-as/strings.xml
@@ -19,7 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4567566098528588863">"ছেটিংছসমূহৰ সঞ্চয়াগাৰ"</string>
-    <string name="wifi_softap_config_change" msgid="5688373762357941645">"হটস্পটৰ ছেটিংসমূহ সলনি হৈছে"</string>
+    <string name="app_label" msgid="4567566098528588863">"ছেটিঙৰ ষ্ট\'ৰেজ"</string>
+    <string name="wifi_softap_config_change" msgid="5688373762357941645">"হটস্পটৰ ছেটিং সলনি হৈছে"</string>
     <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"সবিশেষ চাবলৈ টিপক"</string>
 </resources>
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index b8039e1..e026012 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -37,8 +37,7 @@
 
 const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
 
-const val MIN_CHROMA = 15
-const val MIN_LSTAR = 10
+const val MIN_CHROMA = 5
 
 public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
 
@@ -75,7 +74,14 @@
         get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
 
     init {
-        val seedArgb = if (seed == Color.TRANSPARENT) GOOGLE_BLUE else seed
+        val proposedSeedCam = Cam.fromInt(seed)
+        val seedArgb = if (seed == Color.TRANSPARENT) {
+            GOOGLE_BLUE
+        } else if (proposedSeedCam.chroma < 5) {
+            GOOGLE_BLUE
+        } else {
+            seed
+        }
         val camSeed = Cam.fromInt(seedArgb)
         val hue = camSeed.hue
         val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
@@ -129,9 +135,7 @@
                 val distinctColors = wallpaperColors.mainColors.map {
                     it.toArgb()
                 }.distinct().filter {
-                    val cam = Cam.fromInt(it)
-                    val lstar = lstarFromInt(it)
-                    cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR
+                    Cam.fromInt(it).chroma >= MIN_CHROMA
                 }.toList()
 
                 if (distinctColors.isEmpty()) {
@@ -164,30 +168,38 @@
                 val cam = it.value
                 val lstar = lstarFromInt(it.key)
                 val proportion = intToHueProportion[it.key]!!
-                cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR &&
+                cam.chroma >= MIN_CHROMA &&
                         (totalPopulationMeaningless || proportion > 0.01)
             }
             // Sort the colors by score, from high to low.
-            val seeds = mutableListOf<Int>()
             val intToScoreIntermediate = filteredIntToCam.mapValues {
                 score(it.value, intToHueProportion[it.key]!!)
             }
             val intToScore = intToScoreIntermediate.entries.toMutableList()
             intToScore.sortByDescending { it.value }
 
-            // Go through the colors, from high score to low score. If there isn't already a seed
-            // color with a hue close to color being examined, add the color being examined to the
-            // seed colors.
-            for (entry in intToScore) {
-                val int = entry.key
-                val existingSeedNearby = seeds.find {
-                    val hueA = intToCam[int]!!.hue
-                    val hueB = intToCam[it]!!.hue
-                    hueDiff(hueA, hueB) < 15 } != null
-                if (existingSeedNearby) {
-                    continue
+            // Go through the colors, from high score to low score.
+            // If the color is distinct in hue from colors picked so far, pick the color.
+            // Iteratively decrease the amount of hue distinctness required, thus ensuring we
+            // maximize difference between colors.
+            val minimumHueDistance = 15
+            val seeds = mutableListOf<Int>()
+            maximizeHueDistance@ for (i in 90 downTo minimumHueDistance step 1) {
+                seeds.clear()
+                for (entry in intToScore) {
+                    val int = entry.key
+                    val existingSeedNearby = seeds.find {
+                        val hueA = intToCam[int]!!.hue
+                        val hueB = intToCam[it]!!.hue
+                        hueDiff(hueA, hueB) < i } != null
+                    if (existingSeedNearby) {
+                        continue
+                    }
+                    seeds.add(int)
+                    if (seeds.size >= 4) {
+                        break@maximizeHueDistance
+                    }
                 }
-                seeds.add(int)
             }
 
             if (seeds.isEmpty()) {
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
index 498b7dd..aab3538 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
@@ -53,10 +53,15 @@
      */
     public static @ColorInt int[] of(float hue, float chroma) {
         int[] shades = new int[12];
-        shades[0] = ColorUtils.CAMToColor(hue, chroma, 99);
-        shades[1] = ColorUtils.CAMToColor(hue, chroma, 95);
+        // At tone 90 and above, blue and yellow hues can reach a much higher chroma.
+        // To preserve a consistent appearance across all hues, use a maximum chroma of 40.
+        shades[0] = ColorUtils.CAMToColor(hue, Math.min(40f, chroma), 99);
+        shades[1] = ColorUtils.CAMToColor(hue, Math.min(40f, chroma), 95);
         for (int i = 2; i < 12; i++) {
             float lStar = (i == 6) ? MIDDLE_LSTAR : 100 - 10 * (i - 1);
+            if (lStar >= 90) {
+                chroma = Math.min(40f, chroma);
+            }
             shades[i] = ColorUtils.CAMToColor(hue, chroma, lStar);
         }
         return shades;
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
new file mode 100644
index 0000000..495fbb8
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_media_recommendation_bg_gradient.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="24dp"/>
+    <gradient
+        android:angle="0"
+        android:startColor="#00000000"
+        android:endColor="#ff000000"
+        android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index a2ae5023..e854b02 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -104,4 +104,11 @@
          allow it to use the whole screen space, 0.6 will allow it to use just under half of the
          screen. -->
     <item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item>
+
+    <!-- The actual amount of translation that is applied to the bouncer when it animates from one
+         side of the screen to the other in one-handed mode. Note that it will always translate from
+         the side of the screen to the other (it will "jump" closer to the destination while the
+         opacity is zero), but this controls how much motion will actually be applied to it while
+         animating. Larger values will cause it to move "faster" while fading out/in. -->
+    <dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/color/docked_divider_background.xml b/packages/SystemUI/res/color/docked_divider_background.xml
index 85ede9a..2ab8ecd 100644
--- a/packages/SystemUI/res/color/docked_divider_background.xml
+++ b/packages/SystemUI/res/color/docked_divider_background.xml
@@ -14,5 +14,5 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-  <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+  <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
 </selector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index 6725a26..e626675 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -14,19 +14,20 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:shape="rectangle">
-    <solid
-        android:color="?androidprv:attr/colorSurface"
-        />
-    <corners android:radius="20dp" />
-
-    <padding
-        android:left="20dp"
-        android:right="20dp">
-    </padding>
-
-</shape>
\ No newline at end of file
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <inset
+            android:insetBottom="4dp"
+            android:insetTop="4dp">
+            <shape android:shape="rectangle">
+                <corners android:radius="20dp" />
+                <padding
+                    android:left="20dp"
+                    android:right="20dp" />
+                <solid android:color="?androidprv:attr/colorSurface" />
+            </shape>
+        </inset>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index b841419..918635d 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -38,8 +38,7 @@
             android:gravity="center_vertical|center_horizontal"
             android:layout_width="wrap_content"
             android:layout_height="32dp"
-            android:textColor="?android:attr/textColorPrimary"
-            android:fontFamily="google-sans"
+            android:textAppearance="@style/TextAppearance.InternetDialog"
             android:textSize="24sp"/>
 
         <TextView
@@ -50,8 +49,7 @@
             android:layout_marginTop="4dp"
             android:ellipsize="end"
             android:maxLines="1"
-            android:fontFamily="google-sans"
-            android:textSize="14sp"/>
+            android:textAppearance="@style/TextAppearance.InternetDialog.Secondary"/>
     </LinearLayout>
 
     <LinearLayout
@@ -87,24 +85,49 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
             <LinearLayout
-                android:id="@+id/internet_list"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
 
                 <LinearLayout
+                    android:id="@+id/ethernet_layout"
+                    style="@style/InternetDialog.Network"
+                    android:background="@drawable/settingslib_switch_bar_bg_on"
+                    android:visibility="gone">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:layout_gravity="center_vertical|start"
+                        android:clickable="false">
+                        <ImageView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:autoMirrored="true"
+                            android:src="@drawable/stat_sys_ethernet_fully"
+                            android:tint="@color/connected_network_primary_color"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:layout_weight="1"
+                        android:gravity="start|center_vertical"
+                        android:orientation="vertical"
+                        android:clickable="false">
+                        <TextView
+                            android:text="@string/ethernet_label"
+                            style="@style/InternetDialog.NetworkTitle.Active"/>
+                        <TextView
+                            android:text="@string/to_switch_networks_disconnect_ethernet"
+                            style="@style/InternetDialog.NetworkSummary.Active"/>
+                    </LinearLayout>
+                </LinearLayout>
+
+                <LinearLayout
                     android:id="@+id/mobile_network_layout"
-                    android:layout_width="match_parent"
-                    android:layout_height="88dp"
-                    android:clickable="true"
-                    android:focusable="true"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:layout_gravity="center_vertical|start"
-                    android:orientation="horizontal"
-                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
-                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                    android:paddingStart="22dp"
-                    android:paddingEnd="22dp">
+                    style="@style/InternetDialog.Network">
 
                     <FrameLayout
                         android:layout_width="24dp"
@@ -121,7 +144,6 @@
 
                     <LinearLayout
                         android:layout_weight="1"
-                        android:id="@+id/mobile_network_list"
                         android:orientation="vertical"
                         android:clickable="false"
                         android:layout_width="wrap_content"
@@ -129,28 +151,10 @@
                         android:gravity="start|center_vertical">
                         <TextView
                             android:id="@+id/mobile_title"
-                            android:textDirection="locale"
-                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                            android:layout_marginEnd="7dp"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center_vertical|start"
-                            android:ellipsize="end"
-                            android:textColor="?android:attr/textColorPrimary"
-                            android:textSize="16sp"
-                            android:fontFamily="google-sans"/>
+                            style="@style/InternetDialog.NetworkTitle"/>
                         <TextView
                             android:id="@+id/mobile_summary"
-                            android:textDirection="locale"
-                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                            android:layout_marginEnd="34dp"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center_vertical|start"
-                            android:ellipsize="end"
-                            android:textColor="?android:attr/textColorTertiary"
-                            android:textSize="14sp"
-                            android:fontFamily="google-sans"/>
+                            style="@style/InternetDialog.NetworkSummary"/>
                     </LinearLayout>
 
                     <FrameLayout
@@ -159,10 +163,11 @@
                         android:layout_gravity="end|center_vertical">
                         <Switch
                             android:id="@+id/mobile_toggle"
+                            android:contentDescription="@string/mobile_data_settings_title"
                             android:switchMinWidth="@dimen/settingslib_switch_track_width"
                             android:layout_gravity="center"
                             android:layout_width="@dimen/settingslib_switch_track_width"
-                            android:layout_height="@dimen/settingslib_switch_track_height"
+                            android:layout_height="match_parent"
                             android:track="@drawable/settingslib_track_selector"
                             android:thumb="@drawable/settingslib_thumb_selector"
                             android:theme="@style/MainSwitch.Settingslib"/>
@@ -172,17 +177,11 @@
 
                 <LinearLayout
                     android:id="@+id/turn_on_wifi_layout"
-                    android:layout_width="match_parent"
+                    style="@style/InternetDialog.Network"
                     android:layout_height="72dp"
-                    android:clickable="true"
-                    android:focusable="true"
-                    android:background="?android:attr/selectableItemBackground"
                     android:gravity="center"
-                    android:orientation="horizontal"
-                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
-                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                    android:paddingStart="22dp"
-                    android:paddingEnd="22dp">
+                    android:clickable="false"
+                    android:focusable="false">
 
                     <FrameLayout
                         android:layout_weight="1"
@@ -193,13 +192,10 @@
                         <TextView
                             android:id="@+id/wifi_toggle_title"
                             android:text="@string/turn_on_wifi"
-                            android:textDirection="locale"
                             android:layout_width="wrap_content"
                             android:layout_height="match_parent"
                             android:gravity="start|center_vertical"
-                            android:textColor="?android:attr/textColorPrimary"
-                            android:textSize="16sp"
-                            android:fontFamily="google-sans"/>
+                            android:textAppearance="@style/TextAppearance.InternetDialog"/>
                     </FrameLayout>
 
                     <FrameLayout
@@ -209,10 +205,11 @@
                         android:layout_marginBottom="10dp">
                         <Switch
                             android:id="@+id/wifi_toggle"
+                            android:contentDescription="@string/turn_on_wifi"
                             android:switchMinWidth="@dimen/settingslib_switch_track_width"
                             android:layout_gravity="center"
                             android:layout_width="@dimen/settingslib_switch_track_width"
-                            android:layout_height="@dimen/settingslib_switch_track_height"
+                            android:layout_height="match_parent"
                             android:track="@drawable/settingslib_track_selector"
                             android:thumb="@drawable/settingslib_thumb_selector"
                             android:theme="@style/MainSwitch.Settingslib"/>
@@ -222,18 +219,12 @@
 
                 <LinearLayout
                     android:id="@+id/wifi_connected_layout"
-                    android:layout_width="match_parent"
+                    style="@style/InternetDialog.Network"
                     android:layout_height="72dp"
-                    android:layout_gravity="center_vertical|start"
-                    android:clickable="true"
-                    android:focusable="true"
-                    android:visibility="gone"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:orientation="horizontal"
-                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
                     android:paddingStart="20dp"
-                    android:paddingEnd="24dp">
+                    android:paddingEnd="24dp"
+                    android:background="@drawable/settingslib_switch_bar_bg_on"
+                    android:visibility="gone">
 
                     <FrameLayout
                         android:layout_width="24dp"
@@ -248,7 +239,6 @@
                     </FrameLayout>
 
                     <LinearLayout
-                        android:id="@+id/wifi_connected_list"
                         android:orientation="vertical"
                         android:clickable="false"
                         android:layout_width="wrap_content"
@@ -258,26 +248,11 @@
                         android:gravity="start|center_vertical">
                         <TextView
                             android:id="@+id/wifi_connected_title"
-                            android:textDirection="locale"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center_vertical|start"
-                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                            android:ellipsize="end"
-                            android:textColor="?android:attr/textColorPrimary"
-                            android:textSize="14sp"
-                            android:fontFamily="google-sans"/>
+                            style="@style/InternetDialog.NetworkTitle.Active"
+                            android:textSize="14sp"/>
                         <TextView
                             android:id="@+id/wifi_connected_summary"
-                            android:textDirection="locale"
-                            android:layout_width="wrap_content"
-                            android:layout_height="wrap_content"
-                            android:layout_gravity="center_vertical|start"
-                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
-                            android:ellipsize="end"
-                            android:textColor="?android:attr/textColorTertiary"
-                            android:textSize="14sp"
-                            android:fontFamily="google-sans"/>
+                            style="@style/InternetDialog.NetworkSummary.Active"/>
                     </LinearLayout>
 
                     <FrameLayout
@@ -340,33 +315,33 @@
                     android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
                     <TextView
                         android:text="@string/see_all_networks"
-                        android:textDirection="locale"
                         android:layout_width="wrap_content"
                         android:layout_height="match_parent"
                         android:gravity="start|center_vertical"
-                        android:textColor="?android:attr/textColorPrimary"
-                        android:textSize="14sp"
-                        android:fontFamily="google-sans"/>
+                        android:textAppearance="@style/TextAppearance.InternetDialog"
+                        android:textSize="14sp"/>
                 </FrameLayout>
-
             </LinearLayout>
 
             <FrameLayout
-                android:layout_width="match_parent"
+                android:id="@+id/done_layout"
+                android:layout_width="67dp"
                 android:layout_height="48dp"
-                android:layout_marginBottom="40dp">
+                android:layout_marginEnd="24dp"
+                android:layout_marginBottom="40dp"
+                android:layout_gravity="end|center_vertical"
+                android:clickable="true"
+                android:focusable="true">
                 <Button
-                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-                    android:id="@+id/done"
-                    android:layout_width="67dp"
-                    android:layout_height="36dp"
-                    android:layout_marginEnd="24dp"
-                    android:layout_gravity="end|center_vertical"
-                    android:background="@drawable/internet_dialog_footer_background"
-                    android:textColor="?android:attr/textColorPrimary"
                     android:text="@string/inline_done_button"
+                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                    android:layout_width="match_parent"
+                    android:layout_height="36dp"
+                    android:layout_gravity="center"
+                    android:textAppearance="@style/TextAppearance.InternetDialog"
                     android:textSize="14sp"
-                    android:fontFamily="google-sans"/>
+                    android:background="@drawable/internet_dialog_footer_background"
+                    android:clickable="false"/>
             </FrameLayout>
         </LinearLayout>
     </androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 05352c5..868331e 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -24,21 +24,15 @@
 
     <LinearLayout
         android:id="@+id/wifi_list"
-        android:layout_width="match_parent"
+        style="@style/InternetDialog.Network"
         android:layout_height="72dp"
-        android:layout_gravity="center_vertical|start"
-        android:clickable="true"
-        android:focusable="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:orientation="horizontal"
         android:paddingStart="20dp"
-        android:paddingEnd="40dp">
+        android:paddingEnd="24dp">
         <FrameLayout
             android:layout_width="24dp"
             android:layout_height="24dp"
             android:clickable="false"
-            android:layout_gravity="center_vertical|start"
-            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+            android:layout_gravity="center_vertical|start">
             <ImageView
                 android:id="@+id/wifi_icon"
                 android:layout_width="wrap_content"
@@ -52,31 +46,16 @@
             android:clickable="false"
             android:layout_width="wrap_content"
             android:layout_height="72dp"
+            android:layout_marginEnd="30dp"
             android:layout_weight="1"
-            android:gravity="start|center_vertical"
-            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+            android:gravity="start|center_vertical">
             <TextView
                 android:id="@+id/wifi_title"
-                android:textDirection="locale"
-                android:layout_width="wrap_content"
-                android:layout_height="20dp"
-                android:gravity="start|center_vertical"
-                android:ellipsize="end"
-                android:textColor="?android:attr/textColorPrimary"
-                android:textSize="14sp"
-                android:fontFamily="google-sans"
-                android:layout_marginEnd="18dp"/>
+                style="@style/InternetDialog.NetworkTitle"
+                android:textSize="14sp"/>
             <TextView
                 android:id="@+id/wifi_summary"
-                android:textDirection="locale"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="start|center_vertical"
-                android:ellipsize="end"
-                android:textColor="?android:attr/textColorSecondary"
-                android:textSize="14sp"
-                android:fontFamily="google-sans"
-                android:layout_marginEnd="18dp"/>
+                style="@style/InternetDialog.NetworkSummary"/>
         </LinearLayout>
 
         <FrameLayout
diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
index a21a63c..9cf09ff 100644
--- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
+++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
@@ -15,19 +15,25 @@
   ~ limitations under the License
   -->
 <!-- This is a view that shows a user switcher in Keyguard. -->
-<com.android.systemui.statusbar.phone.UserAvatarView
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/keyguard_qs_user_switch_view"
-    android:layout_width="@dimen/kg_framed_avatar_size"
-    android:layout_height="@dimen/kg_framed_avatar_size"
-    android:layout_centerHorizontal="true"
-    android:layout_gravity="top|end"
-    android:layout_marginEnd="16dp"
-    systemui:avatarPadding="0dp"
-    systemui:badgeDiameter="18dp"
-    systemui:badgeMargin="1dp"
-    systemui:frameColor="@color/kg_user_avatar_frame"
-    systemui:framePadding="0dp"
-    systemui:frameWidth="0dp">
-</com.android.systemui.statusbar.phone.UserAvatarView>
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="end">
+    <com.android.systemui.statusbar.phone.UserAvatarView
+        android:id="@+id/kg_multi_user_avatar"
+        android:layout_width="@dimen/kg_framed_avatar_size"
+        android:layout_height="@dimen/kg_framed_avatar_size"
+        android:layout_centerHorizontal="true"
+        android:layout_gravity="top|end"
+        android:layout_marginEnd="16dp"
+        systemui:avatarPadding="0dp"
+        systemui:badgeDiameter="18dp"
+        systemui:badgeMargin="1dp"
+        systemui:frameColor="@color/kg_user_avatar_frame"
+        systemui:framePadding="0dp"
+        systemui:frameWidth="0dp">
+    </com.android.systemui.statusbar.phone.UserAvatarView>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index c7e54d4..c3fc669 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -48,20 +48,41 @@
         android:id="@+id/recommendation_card_icon"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
+        android:layout_marginTop="@dimen/qs_media_padding"
         android:src="@drawable/ic_headset"
-        style="@style/MediaPlayer.AppIcon"/>
+        style="@style/MediaPlayer.AppIcon"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+        app:layout_constraintHorizontal_bias="0"/>
 
     <TextView
         android:id="@+id/recommendation_card_text"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:maxLines="2"
+        android:maxLines="1"
         android:text="@string/controls_media_smartspace_rec_title"
         android:fontFamily="google-sans-medium"
         android:textDirection="locale"
         android:textSize="@dimen/qq_aa_media_rec_header_text_size"
-        android:breakStrategy="balanced"
-        android:hyphenationFrequency="none"/>
+        android:hyphenationFrequency="none"
+        app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+        app:layout_constraintHorizontal_bias="0"/>
+
+    <View
+        android:id="@+id/recommendation_gradient_view"
+        android:layout_width="@dimen/qs_aa_media_gradient_bg_width"
+        android:layout_height="0dp"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:background="@drawable/qs_media_recommendation_bg_gradient"
+        app:layout_constraintTop_toTopOf="@id/recommendation_card_text"
+        app:layout_constraintBottom_toBottomOf="@id/recommendation_card_text"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
+        app:layout_constraintHorizontal_bias="1"/>
 
     <FrameLayout
         android:id="@+id/media_cover1_container"
diff --git a/packages/SystemUI/res/layout/privacy_dialog.xml b/packages/SystemUI/res/layout/privacy_dialog.xml
index 459fb66..ee4530c 100644
--- a/packages/SystemUI/res/layout/privacy_dialog.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog.xml
@@ -24,9 +24,9 @@
     android:layout_marginEnd="@dimen/ongoing_appops_dialog_side_margins"
     android:layout_marginTop="8dp"
     android:orientation="vertical"
-    android:paddingBottom="12dp"
-    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:paddingTop="12dp"
+    android:paddingHorizontal="@dimen/ongoing_appops_dialog_side_padding"
     android:background="@drawable/qs_dialog_bg"
 />
-<!-- 12dp padding bottom so there's 20dp total under the icon -->
-<!-- 8dp padding top, as there's 4dp margin in each row -->
\ No newline at end of file
+<!-- 8dp padding bottom so there's 16dp total under the icon -->
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/privacy_dialog_item.xml b/packages/SystemUI/res/layout/privacy_dialog_item.xml
index 7c8945e..e1f0793 100644
--- a/packages/SystemUI/res/layout/privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/privacy_dialog_item.xml
@@ -24,8 +24,6 @@
     android:layout_marginTop="4dp"
     android:importantForAccessibility="yes"
     android:background="?android:attr/selectableItemBackground"
-    android:paddingLeft="@dimen/ongoing_appops_dialog_side_padding"
-    android:paddingRight="@dimen/ongoing_appops_dialog_side_padding"
     android:focusable="true"
     >
     <!-- 4dp marginTop makes 20dp minimum between icons -->
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index fc0d60a..542a1c9 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -39,7 +39,6 @@
             android:id="@+id/clock"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:minWidth="48dp"
             android:minHeight="@dimen/qs_header_row_min_height"
             android:gravity="center_vertical|start"
             android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
@@ -51,6 +50,7 @@
             android:id="@+id/date_clock"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
+            android:layout_marginStart="@dimen/status_bar_left_clock_end_padding"
             android:gravity="center_vertical|start"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.QS.Status"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4355751..4b6e58f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -70,6 +70,19 @@
             android:padding="@dimen/lock_icon_padding"
             android:layout_gravity="center"
             android:scaleType="centerCrop"/>
+
+        <!-- Fingerprint -->
+        <!-- AOD dashed fingerprint icon with moving dashes -->
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/lock_udfps_aod_fp"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="@dimen/lock_icon_padding"
+            android:layout_gravity="center"
+            android:scaleType="centerCrop"
+            systemui:lottie_autoPlay="false"
+            systemui:lottie_loop="true"
+            systemui:lottie_rawRes="@raw/udfps_aod_fp"/>
     </com.android.keyguard.LockIconView>
 
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 412276d..bbb8df1c 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -30,8 +30,8 @@
             style="@style/TextAppearance.NotificationSectionHeaderButton"
             android:id="@+id/manage_text"
             android:layout_width="wrap_content"
-            android:layout_height="40dp"
-            android:layout_marginTop="16dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="12dp"
             android:layout_gravity="start"
             android:background="@drawable/notif_footer_btn_background"
             android:focusable="true"
@@ -43,8 +43,8 @@
             style="@style/TextAppearance.NotificationSectionHeaderButton"
             android:id="@+id/dismiss_text"
             android:layout_width="wrap_content"
-            android:layout_height="40dp"
-            android:layout_marginTop="16dp"
+            android:layout_height="48dp"
+            android:layout_marginTop="12dp"
             android:layout_gravity="end"
             android:background="@drawable/notif_footer_btn_background"
             android:focusable="true"
diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml
index 49b182a..a3fe8ef 100644
--- a/packages/SystemUI/res/layout/text_toast.xml
+++ b/packages/SystemUI/res/layout/text_toast.xml
@@ -45,6 +45,5 @@
         android:maxLines="2"
         android:paddingTop="12dp"
         android:paddingBottom="12dp"
-        android:lineHeight="20sp"
         android:textAppearance="@*android:style/TextAppearance.Toast"/>
 </LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index bfb6969..5cd99f2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index df4eafc..2f485ad 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 28c2d7c..81a7f7c 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1200,4 +1200,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a5d599c..d73fd73 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -37,7 +37,7 @@
     <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"বেটাৰী সঞ্চয়কাৰীৰ বিষয়ে"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"অন কৰক"</string>
     <string name="battery_saver_start_action" msgid="4553256017945469937">"বেটাৰি সঞ্চয়কাৰী অন কৰক"</string>
-    <string name="status_bar_settings_settings_button" msgid="534331565185171556">"ছেটিংসমূহ"</string>
+    <string name="status_bar_settings_settings_button" msgid="534331565185171556">"ছেটিং"</string>
     <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"ৱাই-ফাই"</string>
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
     <string name="status_bar_settings_mute_label" msgid="914392730086057522">"মিউট"</string>
@@ -233,7 +233,7 @@
     <string name="accessibility_battery_level" msgid="5143715405241138822">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰি।"</string>
     <string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰী <xliff:g id="PERCENTAGE">%1$s</xliff:g> শতাংশ, প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
     <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"বেটাৰি চাৰ্জ হৈ আছে, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> শতাংশ।"</string>
-    <string name="accessibility_settings_button" msgid="2197034218538913880">"ছিষ্টেমৰ ছেটিংসমূহ৷"</string>
+    <string name="accessibility_settings_button" msgid="2197034218538913880">"ছিষ্টেমৰ ছেটিং৷"</string>
     <string name="accessibility_notifications_button" msgid="3960913924189228831">"জাননীসমূহ।"</string>
     <string name="accessibility_overflow_action" msgid="8555835828182509104">"সকলো জাননীবোৰ চাওক"</string>
     <string name="accessibility_remove_notification" msgid="1641455251495815527">"জাননী মচক৷"</string>
@@ -248,9 +248,9 @@
     <skip />
     <string name="accessibility_notification_dismissed" msgid="4411652015138892952">"জাননী অগ্ৰাহ্য কৰা হৈছে।"</string>
     <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"জাননী পেনেল।"</string>
-    <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিংসমূহ।"</string>
+    <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিং।"</string>
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীন।"</string>
-    <string name="accessibility_desc_settings" msgid="6728577365389151969">"ছেটিংসমূহ"</string>
+    <string name="accessibility_desc_settings" msgid="6728577365389151969">"ছেটিং"</string>
     <string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"অৱলোকন।"</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীন"</string>
     <string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ কৰক"</string>
@@ -318,8 +318,8 @@
       <item quantity="other"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item>
     </plurals>
     <string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
-    <string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"জাননীৰ ছেটিংসমূহ"</string>
-    <string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিংসমূহ"</string>
+    <string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"জাননীৰ ছেটিং"</string>
+    <string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিং"</string>
     <string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"আপোনাৰ ফ\'নৰ স্ক্ৰীন স্বয়ংক্ৰিয়ভাৱে ঘূৰিব।"</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"স্ক্ৰীন লেণ্ডস্কে\'প দিশত লক কৰা হ’ল।"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"স্ক্ৰীন প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
@@ -361,7 +361,7 @@
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"মিডিয়া ডিভাইচ"</string>
     <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"জৰুৰীকালীন কল মাত্ৰ"</string>
-    <string name="quick_settings_settings_label" msgid="2214639529565474534">"ছেটিংসমূহ"</string>
+    <string name="quick_settings_settings_label" msgid="2214639529565474534">"ছেটিং"</string>
     <string name="quick_settings_time_label" msgid="3352680970557509303">"সময়"</string>
     <string name="quick_settings_user_label" msgid="1253515509432672496">"মোক"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string>
@@ -555,9 +555,9 @@
     <string name="disconnect_vpn" msgid="26286850045344557">"ভিপিএন সংযোগ বিচ্ছিন্ন কৰক"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"নীতিসমূহ চাওক"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"নিয়ন্ত্ৰণসমূহ চাওক"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্‌সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিং, কৰ্পৰে’টৰ এক্সেছ, এপ্‌, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>এ হয়তো এই ডিভাইচটোৰ সৈতে জড়িত হৈ থকা ডেটা এক্সেছ কৰিব, এপ্‌ পৰিচালনা কৰিব আৰু এই ডিভাইচটোৰ ছেটিং সলনি কৰিব পাৰিব।\n\nআপোনাৰ যদি কিবা প্ৰশ্ন আছে, তেন্তে <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ৰ সৈতে যোগাযোগ কৰক।"</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিংসমূহ, কৰ্পৰে’টৰ এক্সেছ, এপ্‌সমূহ, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ।\n\nআপোনাৰ আইটি প্ৰশাসকে আপোনাৰ ডিভাইচটোৰ লগত জড়িত ছেটিং, কৰ্পৰে’টৰ এক্সেছ, এপ্‌, ডেটা আৰু আপোনাৰ ডিভাইচটোৰ অৱস্থান সম্পৰ্কীয় তথ্য নিৰীক্ষণ কৰাৰ লগতে সেয়া পৰিচালনা কৰিব পাৰে।\n\nঅধিক তথ্যৰ বাবে আপোনাৰ আইটি প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"আপোনাৰ প্ৰতিষ্ঠানে এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰা হৈছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string>
@@ -569,12 +569,12 @@
     <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"আপোনাৰ ব্যক্তিগত প্ৰ\'ফাইলটো <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে, যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
     <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"আপোনাৰ ডিভাইচটো <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>ৰ দ্বাৰা পৰিচালিত।"</string>
     <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>এ আপোনাৰ ডিভাইচটো পৰিচালনা কৰিবলৈ <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ব্যৱহাৰ কৰে।"</string>
-    <string name="monitoring_description_do_body" msgid="7700878065625769970">"আপোনাৰ প্ৰশাসকে আপোনাৰ ডিভাইচৰ লগত জড়িত ছেটিংসমূহ, কৰ্প\'ৰেইট অনুমতি, এপসমূহ, ডেটা আৰু ডিভাইচৰ অৱস্থান সম্পৰ্কীয় তথ্য পৰ্যবেক্ষণ কৰাৰ লগতে পৰিচালনা কৰিব পাৰিব।"</string>
+    <string name="monitoring_description_do_body" msgid="7700878065625769970">"আপোনাৰ প্ৰশাসকে আপোনাৰ ডিভাইচৰ লগত জড়িত ছেটিং, কৰ্প\'ৰেইট এক্সেছ, এপ্‌, ডেটা আৰু ডিভাইচৰ অৱস্থান সম্পৰ্কীয় তথ্য পৰ্যবেক্ষণ তথা পৰিচালনা কৰিব পাৰে।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="645149183455573790">"অধিক জানক"</string>
     <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g> ৰে সংযুক্ত হৈ আছে, ই ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি আপোনাৰ নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
-    <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"ভিপিএন ছেটিংসমূহ খোলক"</string>
+    <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN ছেটিং খোলক"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"বিশ্বাসী পৰিচয়-পত্ৰসমূহ খোলক"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ ডিভাইচটোত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰে।\n\nএই সম্পৰ্কে অধিক জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
@@ -596,7 +596,7 @@
     <string name="hidden_notifications_setup" msgid="2064795578526982467">"ছেট আপ কৰক"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="volume_zen_end_now" msgid="5901885672973736563">"এতিয়া অফ কৰক"</string>
-    <string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিংসমূহ"</string>
+    <string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিং"</string>
     <string name="accessibility_volume_expand" msgid="7653070939304433603">"সম্প্ৰসাৰণ কৰক"</string>
     <string name="accessibility_volume_collapse" msgid="2746845391013829996">"সংকুচিত কৰক"</string>
     <string name="volume_odi_captions_tip" msgid="8825655463280990941">"স্বয়ংক্ৰিয় কেপশ্বন মিডিয়া"</string>
@@ -621,7 +621,7 @@
     <string name="screen_pinning_start" msgid="7483998671383371313">"এপ্‌টো পিন কৰা হ’ল"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"এপ্‌টো আনপিন কৰা হ’ল"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকুৱাবনে?"</string>
-    <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"আপুনি ইয়াক পৰৱৰ্তী সময়ত ছেটিংসমূহত অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
+    <string name="quick_settings_reset_confirmation_message" msgid="2320586180785674186">"আপুনি পৰৱৰ্তী সময়ত ছেটিঙত ইয়াক অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
     <string name="quick_settings_reset_confirmation_button" msgid="3341477479055016776">"লুকুৱাওক"</string>
     <string name="stream_voice_call" msgid="7468348170702375660">"কল"</string>
     <string name="stream_system" msgid="7663148785370565134">"ছিষ্টেম"</string>
@@ -659,7 +659,7 @@
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="show_battery_percentage" msgid="6235377891802910455">"সংযুক্ত বেটাৰিৰ কিমান শতাংশ বাকী আছে দেখুওৱাক"</string>
     <string name="show_battery_percentage_summary" msgid="9053024758304102915">"চাৰ্জ হৈ নথকা অৱস্থাত বেটাৰি কিমান শতাংশ বাকী স্থিতি দণ্ডৰ ভিতৰত দেখুৱাওক"</string>
-    <string name="quick_settings" msgid="6211774484997470203">"ক্ষিপ্ৰ ছেটিংসমূহ"</string>
+    <string name="quick_settings" msgid="6211774484997470203">"ক্ষিপ্ৰ ছেটিং"</string>
     <string name="status_bar" msgid="4357390266055077437">"স্থিতি দণ্ড"</string>
     <string name="overview" msgid="3522318590458536816">"অৱলোকন"</string>
     <string name="demo_mode" msgid="263484519766901593">"ছিষ্টেমৰ UI প্ৰদৰ্শন ম\'ড"</string>
@@ -684,21 +684,21 @@
     <string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
     <string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
-    <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"ক্ষিপ্ৰ ছেটিংসমূহ, <xliff:g id="TITLE">%s</xliff:g>।"</string>
+    <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"ক্ষিপ্ৰ ছেটিং, <xliff:g id="TITLE">%s</xliff:g>।"</string>
     <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"হটস্পট"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
     <string name="tuner_persistent_warning" msgid="230466285569307806">"এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
     <string name="got_it" msgid="477119182261892069">"বুজি পালোঁ"</string>
-    <string name="tuner_toast" msgid="3812684836514766951">"অভিনন্দন! ছেটিংসমূহত System UI Tuner যোগ কৰা হৈছে"</string>
-    <string name="remove_from_settings" msgid="633775561782209994">"ছেটিংসমূহৰ পৰা আঁতৰাওক"</string>
-    <string name="remove_from_settings_prompt" msgid="551565437265615426">"ছেটিংসমূহৰ পৰা System UI Tuner আঁতৰাই ইয়াৰ সুবিধাসমূহ ব্যৱহাৰ কৰাটো বন্ধ কৰিবনে?"</string>
+    <string name="tuner_toast" msgid="3812684836514766951">"অভিনন্দন! ছেটিঙত System UI Tuner যোগ কৰা হৈছে"</string>
+    <string name="remove_from_settings" msgid="633775561782209994">"ছেটিঙৰ পৰা আঁতৰাওক"</string>
+    <string name="remove_from_settings_prompt" msgid="551565437265615426">"ছেটিঙৰ পৰা System UI Tuner আঁতৰাই ইয়াৰ সুবিধাসমূহ ব্যৱহাৰ কৰাটো বন্ধ কৰিবনে?"</string>
     <string name="activity_not_found" msgid="8711661533828200293">"আপোনাৰ ডিভাইচত এপ্লিকেশ্বনটো ইনষ্টল কৰা হোৱা নাই"</string>
     <string name="clock_seconds" msgid="8709189470828542071">"ঘড়ীৰ ছেকেণ্ড দেখুৱাওক"</string>
     <string name="clock_seconds_desc" msgid="2415312788902144817">"স্থিতি দণ্ডত ঘড়ীৰ ছেকেণ্ড দেখুৱাওক। এই কার্যই বেটাৰিৰ অৱস্থাত প্ৰভাৱ পেলাব পাৰে।"</string>
-    <string name="qs_rearrange" msgid="484816665478662911">"ক্ষিপ্ৰ ছেটিংসমূহ পুনৰ সজাওক"</string>
-    <string name="show_brightness" msgid="6700267491672470007">"দ্ৰুত ছেটিংসমূহত উজ্জ্বলতা দেখুৱাওক"</string>
+    <string name="qs_rearrange" msgid="484816665478662911">"ক্ষিপ্ৰ ছেটিং পুনৰ সজাওক"</string>
+    <string name="show_brightness" msgid="6700267491672470007">"দ্ৰুত ছেটিঙত উজ্জ্বলতা দেখুৱাওক"</string>
     <string name="experimental" msgid="3549865454812314826">"পৰীক্ষামূলক"</string>
     <string name="enable_bluetooth_title" msgid="866883307336662596">"ব্লুটুথ অন কৰিবনে?"</string>
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"আপোনাৰ টেবলেটত আপোনাৰ কীব\'ৰ্ড সংযোগ কৰিবলৈ আপুনি প্ৰথমে ব্লুটুথ অন কৰিব লাগিব।"</string>
@@ -748,7 +748,7 @@
     <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়"</string>
     <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string>
     <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"বাৰ্তালাপৰ জাননীৰ শীৰ্ষত আৰু প্ৰ’ফাইল চিত্ৰ হিচাপে লক স্ক্ৰীনত দেখুৱায়, এটা বাবল হিচাপে দেখা পোৱা যায়, অসুবিধা নিদিব ম’ডত ব্যাঘাত জন্মায়"</string>
-    <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ছেটিংসমূহ"</string>
+    <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ছেটিং"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"অগ্ৰাধিকাৰ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বাৰ্তালাপৰ সুবিধাসমূহ সমৰ্থন নকৰে"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"এই জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string>
@@ -763,7 +763,7 @@
     <string name="appops_camera_overlay" msgid="6466845606058816484">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
     <string name="appops_mic_overlay" msgid="4609326508944233061">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
     <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
-    <string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিংসমূহ"</string>
+    <string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিং"</string>
     <string name="notification_appops_ok" msgid="2177609375872784124">"ঠিক আছে"</string>
     <string name="feedback_alerted" msgid="5192459808484271208">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া &lt;b&gt;গুৰুত্ব ডিফ’ল্ট&lt;/b&gt;লৈ বৃদ্ধি কৰিছে।"</string>
     <string name="feedback_silenced" msgid="9116540317466126457">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া &lt;b&gt;গুৰুত্ব নীৰৱ&lt;/b&gt;লৈ হ্ৰাস কৰিছে।"</string>
@@ -791,7 +791,7 @@
     <string name="notification_menu_gear_description" msgid="6429668976593634862">"জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
     <string name="notification_menu_snooze_description" msgid="4740133348901973244">"জাননীক স্নুজ কৰাৰ বিকল্পসমূহ"</string>
     <string name="notification_menu_snooze_action" msgid="5415729610393475019">"মোক মনত পেলাই দিব"</string>
-    <string name="notification_menu_settings_action" msgid="7085494017202764285">"ছেটিংসমূহ"</string>
+    <string name="notification_menu_settings_action" msgid="7085494017202764285">"ছেটিং"</string>
     <string name="snooze_undo" msgid="2738844148845992103">"আনডু কৰক"</string>
     <string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ৰ বাবে স্নুজ কৰক"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
@@ -856,7 +856,7 @@
     <string name="battery" msgid="769686279459897127">"বেটাৰি"</string>
     <string name="clock" msgid="8978017607326790204">"ঘড়ী"</string>
     <string name="headset" msgid="4485892374984466437">"হেডছেট"</string>
-    <string name="accessibility_long_click_tile" msgid="210472753156768705">"ছেটিংসমূহ খোলক"</string>
+    <string name="accessibility_long_click_tile" msgid="210472753156768705">"ছেটিং খোলক"</string>
     <string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"হেডফ\'ন সংযোগ হৈ আছে"</string>
     <string name="accessibility_status_bar_headset" msgid="2699275863720926104">"হেডছেট সংযোগ হৈ আছে"</string>
     <string name="data_saver" msgid="3484013368530820763">"ডেটা সঞ্চয়কাৰী"</string>
@@ -920,19 +920,19 @@
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
     <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ দিয়া হৈছে"</string>
     <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল আঁতৰোৱা হৈছে"</string>
-    <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
+    <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিঙৰ সম্পাদক।"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
-    <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ক্ষিপ্ৰ ছেটিংসমূহ খোলক।"</string>
-    <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ক্ষিপ্ৰ ছেটিংসমূহ বন্ধ কৰক।"</string>
+    <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিং খোলক।"</string>
+    <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ক্ষিপ্ৰ ছেটিং খোলক।"</string>
+    <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ক্ষিপ্ৰ ছেটিং বন্ধ কৰক।"</string>
     <string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"এলার্ম ছেট কৰা হ’ল।"</string>
     <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ’ল"</string>
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"বিৱৰণসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g>ৰ বাবে উপলব্ধ নহয়"</string>
-    <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিংসমূহ খোলক।"</string>
-    <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
+    <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিং খোলক।"</string>
+    <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিঙৰ ক্ৰম সম্পাদনা কৰক।"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাৱাৰ মেনু"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীন"</string>
@@ -990,7 +990,7 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"আপুনি <xliff:g id="CARRIER">%s</xliff:g>ৰ জৰিয়তে ডেটা সংযোগ বা ইণ্টাৰনেট সংযোগ নাপাব। কেৱল ৱাই-ফাইৰ যোগেৰে ইণ্টাৰনেট উপলব্ধ হ\'ব।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"আপোনাৰ বাহক"</string>
-    <string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিংসমূহে আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
+    <string name="touch_filtered_warning" msgid="8119511393338714836">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিঙৰ পৰা আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
     <string name="slice_permission_text_2" msgid="6758906940360746983">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ ভিতৰত কাৰ্য কৰিব পাৰে"</string>
@@ -1002,7 +1002,7 @@
     <string name="no_auto_saver_action" msgid="7467924389609773835">"নালাগে, ধন্যবাদ"</string>
     <string name="auto_saver_enabled_title" msgid="4294726198280286333">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী অন কৰা অৱস্থাত আছে"</string>
     <string name="auto_saver_enabled_text" msgid="7889491183116752719">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
-    <string name="open_saver_setting_action" msgid="2111461909782935190">"ছেটিংবোৰ"</string>
+    <string name="open_saver_setting_action" msgid="2111461909782935190">"ছেটিং"</string>
     <string name="auto_saver_okay_action" msgid="7815925750741935386">"বুজি পালোঁ"</string>
     <string name="heap_dump_tile_name" msgid="2464189856478823046">"SysUI হীপ ডাম্প কৰক"</string>
     <string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="TYPES_LIST">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে।"</string>
@@ -1021,8 +1021,8 @@
     <string name="device_services" msgid="1549944177856658705">"ডিভাইচ সেৱা"</string>
     <string name="music_controls_no_title" msgid="4166497066552290938">"কোনো শিৰোনাম নাই"</string>
     <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"আঁতৰাওক"</string>
-    <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিংসমূহ-লৈ যাওক।"</string>
-    <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিংসমূহ-লৈ যাওক"</string>
+    <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিঙলৈ যাওক।"</string>
+    <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিঙলৈ যাওক"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"ষ্টেণ্ডবাই"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
@@ -1065,7 +1065,7 @@
     <string name="controls_favorite_removed" msgid="5276978408529217272">"সকলো নিয়ন্ত্ৰণ আঁতৰোৱা হৈছে"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"সালসলনিসমূহ ছেভ নহ’ল"</string>
     <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"অন্য এপ্‌সমূহ চাওক"</string>
-    <string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্‌টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
+    <string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্‌টোৰ ছেটিং সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"সমিল নিয়ন্ত্ৰণসমূহ উপলব্ধ নহয়"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"অন্য"</string>
     <string name="controls_dialog_title" msgid="2343565267424406202">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহত যোগ দিয়ক"</string>
@@ -1088,7 +1088,7 @@
     <string name="controls_media_active_session" msgid="3146882316024153337">"বৰ্তমানৰ মিডিয়াৰ ছেশ্বনটো লুকুৱাব নোৱাৰি।"</string>
     <string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
-    <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
+    <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিং"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ত <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ৰ <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ হৈ আছে"</string>
     <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"প্লে’ কৰক"</string>
     <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"<xliff:g id="APP_LABEL">%1$s</xliff:g> খোলক"</string>
@@ -1098,7 +1098,7 @@
     <string name="controls_error_retryable" msgid="864025882878378470">"আসোঁৱাহ, পুনৰ চেষ্টা কৰি আছে…"</string>
     <string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
     <string name="controls_error_removed_title" msgid="1207794911208047818">"নিয়ন্ত্ৰণটো উপলব্ধ নহয়"</string>
-    <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> এক্সেছ কৰিব পৰা নগ’ল। নিয়ন্ত্ৰণটো এতিয়াও উপলব্ধ আৰু এপ্‌টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APPLICATION">%2$s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
+    <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g> এক্সেছ কৰিব পৰা নগ’ল। নিয়ন্ত্ৰণটো এতিয়াও উপলব্ধ আৰু এপ্‌টোৰ ছেটিং সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APPLICATION">%2$s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
     <string name="controls_open_app" msgid="483650971094300141">"এপ্‌টো খোলক"</string>
     <string name="controls_error_generic" msgid="352500456918362905">"স্থিতি ল’ড কৰিব নোৱাৰি"</string>
     <string name="controls_error_failed" msgid="960228639198558525">"আসোঁৱাহ হৈছে, আকৌ চেষ্টা কৰক"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7a66f4e..f7746e8 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index d7f37e4..72efc72 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index a5d613c..f7c20ab 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -703,8 +703,8 @@
     <string name="activity_not_found" msgid="8711661533828200293">"Праграма не ўсталявана на вашым тэлефоне"</string>
     <string name="clock_seconds" msgid="8709189470828542071">"Паказваць секунды гадзінніка"</string>
     <string name="clock_seconds_desc" msgid="2415312788902144817">"Паказваць секунды гадзінніка на панэлі стану. Можа паўплываць на рэсурс акумулятара."</string>
-    <string name="qs_rearrange" msgid="484816665478662911">"Змяніць парадак Хуткіх налад"</string>
-    <string name="show_brightness" msgid="6700267491672470007">"Паказваць яркасць у Хуткіх наладах"</string>
+    <string name="qs_rearrange" msgid="484816665478662911">"Змяніць парадак хуткіх налад"</string>
+    <string name="show_brightness" msgid="6700267491672470007">"Паказваць яркасць у хуткіх наладах"</string>
     <string name="experimental" msgid="3549865454812314826">"Эксперыментальныя"</string>
     <string name="enable_bluetooth_title" msgid="866883307336662596">"Уключыць Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"Для падлучэння клавіятуры да планшэта трэба спачатку ўключыць Bluetooth."</string>
@@ -1072,7 +1072,7 @@
     <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"выдаліць з абранага"</string>
     <string name="accessibility_control_move" msgid="8980344493796647792">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%d</xliff:g>"</string>
     <string name="controls_favorite_default_title" msgid="967742178688938137">"Сродкі кіравання"</string>
-    <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Выберыце элементы кіравання, да якіх вы хочаце мець доступ з Хуткіх налад"</string>
+    <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Выберыце элементы кіравання, да якіх вы хочаце мець доступ з хуткіх налад"</string>
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Каб змяніць парадак элементаў кіравання, утрымлівайце і перацягвайце іх"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"Усе элементы кіравання выдалены"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Змяненні не захаваны"</string>
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index eee5adb..b4164a0 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 59cb64c..92da7de 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index af33862..e913d56 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f99ca66..be384e3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a128bf1..5be3077 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index a948a03..2c7ffe5 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4d00de5..9c39acd 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0d5c959..8705ad0 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e47680e..a2bcfd1 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 43f30bb..1cc3cd3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e47680e..a2bcfd1 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e47680e..a2bcfd1 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e0ff8b5..71a1c2f 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎Searching for networks…‎‏‎‎‏‎"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎Failed to connect to network‎‏‎‎‏‎"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎See all‎‏‎‎‏‎"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎To switch networks, disconnect ethernet‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8533c12..0454e73 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 533f368..0751362 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -683,7 +683,7 @@
     <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>) a menos que desactives esta opción antes"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
     <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
-    <string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+    <string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Ajustes rápidos, <xliff:g id="TITLE">%s</xliff:g>."</string>
     <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Compartir Internet"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5bc39f13..1692006 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index aaa1387..8055cfe 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index a97fffb..74f23ac 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 662a6d6..f114407 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 249f126..35ed848 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 77ed144..514c434 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 95f47311..1eba1ed4 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 829b056..2c4e6e5 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 19ec8ce..f057603 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -16,7 +16,7 @@
 
 <resources>
     <!-- Minimum margin between clock and top of screen or ambient indication -->
-    <dimen name="keyguard_clock_top_margin">76dp</dimen>
+    <dimen name="keyguard_clock_top_margin">38dp</dimen>
 
     <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
     <dimen name="large_clock_text_size">200dp</dimen>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index dd4617d..3eb45ea 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index cf465a1..97dbf59 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 20a973b..ddff2c2 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 17d7d75..3100b9c 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a4743c1..80f6ed4 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index d2dbf8d..2732b71 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 81af801..9410c99 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0fa9c9a..f6b74c3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 25f7234..aa6aa3c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2a73634..ac60470 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b09811a..fd2bcc7 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index ff8aadd..035319e 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិន​អាច​ភ្ជាប់​បណ្ដាញ​បានទេ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បី​ប្ដូរ​បណ្ដាញ សូមផ្ដាច់​អ៊ីសឺរណិត"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 07fc111..1d8fbb3 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 7e729d1..751bfd8 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d9f1d2c..3434572 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index c4fa48b..b603ca6 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 3a35355..657c95b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index e5eaac5..f5927db 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 85771e3..396fab5 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index db14ca8..6d92e8a 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്‌വർക്കുകൾ തിരയുന്നു…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്‌വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്‌വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 15414c3..3968d52 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1a4b3a1..69c829c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -688,7 +688,7 @@
     <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"हॉटस्पॉट"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string>
-    <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्‍याला Android वापरकर्ता इंटरफेस ट्विक आणि कस्टमाइझ करण्‍याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्‍ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्‍यातील रिलीज मध्‍ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
+    <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्‍याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्‍याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्‍ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्‍यातील रिलीज मध्‍ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
     <string name="tuner_persistent_warning" msgid="230466285569307806">"ही प्रयोगात्मक वैशिष्‍ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्‍यातील रिलीज मध्‍ये कदाचित दिसणार नाहीत."</string>
     <string name="got_it" msgid="477119182261892069">"समजले"</string>
     <string name="tuner_toast" msgid="3812684836514766951">"अभिनंदन! सिस्टम UI ट्युनर सेटिंग्जमध्‍ये जोडले गेले आहे"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्‍ट करता आले नाही"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 554b087..ebe0094 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index c19dd2f..42ed54c 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5deb612..3337aa1 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d4362d5..a7113f3 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 461505f..ffcc3a8 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -48,4 +48,16 @@
         <item name="android:windowLightStatusBar">false</item>
     </style>
 
+    <style name="TextAppearance.InternetDialog.Active">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/connected_network_primary_color</item>
+        <item name="android:textDirection">locale</item>
+    </style>
+
+    <style name="TextAppearance.InternetDialog.Secondary.Active">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@color/connected_network_secondary_color</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 8c9a878..ea98229 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index b43e902..1ea599f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index d667813..06364ea 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 51fc45e..ae4b5d6 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8109c31..cbd43f8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -592,7 +592,7 @@
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
     <string name="hidden_notifications_title" msgid="1782412844777612795">"Receba notificações mais rápido"</string>
     <string name="hidden_notifications_text" msgid="5899627470450792578">"Veja-as antes de desbloquear"</string>
-    <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Não, obrigado"</string>
+    <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Agora não"</string>
     <string name="hidden_notifications_setup" msgid="2064795578526982467">"Configurar"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
@@ -617,7 +617,7 @@
     <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Para liberar o app, toque nos botões \"Voltar\" e home e os mantenha pressionados"</string>
     <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para liberar o app, deslize para cima e mantenha a tela pressionada"</string>
     <string name="screen_pinning_positive" msgid="3285785989665266984">"Entendi"</string>
-    <string name="screen_pinning_negative" msgid="6882816864569211666">"Não, obrigado"</string>
+    <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 222db52a..a8f6258 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8109c31..cbd43f8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -592,7 +592,7 @@
     <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
     <string name="hidden_notifications_title" msgid="1782412844777612795">"Receba notificações mais rápido"</string>
     <string name="hidden_notifications_text" msgid="5899627470450792578">"Veja-as antes de desbloquear"</string>
-    <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Não, obrigado"</string>
+    <string name="hidden_notifications_cancel" msgid="4805370226181001278">"Agora não"</string>
     <string name="hidden_notifications_setup" msgid="2064795578526982467">"Configurar"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
@@ -617,7 +617,7 @@
     <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Para liberar o app, toque nos botões \"Voltar\" e home e os mantenha pressionados"</string>
     <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Para liberar o app, deslize para cima e mantenha a tela pressionada"</string>
     <string name="screen_pinning_positive" msgid="3285785989665266984">"Entendi"</string>
-    <string name="screen_pinning_negative" msgid="6882816864569211666">"Não, obrigado"</string>
+    <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="463533331480997595">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 95d2647..751ea3a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 58052ac..4452a02 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b52ae93..e87aefb 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0590d31..a1719f0 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 0d1e1fb..7d86eb1 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 8a40a3e..c5eb836 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fb9d3d4..b75535a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1182,4 +1182,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d8e31d2..b0ef225 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 27a25c0..fa1517a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
new file mode 100644
index 0000000..02fd25b
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+
+    <!-- The maximum number of tiles in the QuickQSPanel -->
+    <integer name="quick_qs_panel_max_tiles">6</integer>
+
+    <!-- The maximum number of rows in the QuickSettings -->
+    <integer name="quick_settings_max_rows">3</integer>
+
+    <!-- The number of columns in the QuickSettings -->
+    <integer name="quick_settings_num_columns">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index 40838f3..da2403a 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -16,6 +16,6 @@
   -->
 <resources>
     <!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
-    <dimen name="notification_panel_width">416dp</dimen>
+    <dimen name="notification_panel_width">504dp</dimen>
 
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index 436f8d0..ac44251 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,5 +22,14 @@
 <resources>
     <integer name="status_bar_config_maxNotificationIcons">5</integer>
 
+    <!-- The maximum number of tiles in the QuickQSPanel -->
+    <integer name="quick_qs_panel_max_tiles">6</integer>
+
+    <!-- The number of columns in the QuickSettings -->
+    <integer name="quick_settings_num_columns">3</integer>
+
+    <!-- The maximum number of rows in the QuickSettings -->
+    <integer name="quick_settings_max_rows">3</integer>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 01abf2a..5e6e4c6 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -513,7 +513,7 @@
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> மூலம் ரெக்கார்டிங் செய்யவோ அனுப்புவதற்கோ தொடங்கிவீட்டீர்களா?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"மீண்டும் காட்டாதே"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"எல்லாவற்றையும் அழி"</string>
-    <string name="manage_notifications_text" msgid="6885645344647733116">"அறிவிப்புகளை நிர்வகி"</string>
+    <string name="manage_notifications_text" msgid="6885645344647733116">"நிர்வகி"</string>
     <string name="manage_notifications_history_text" msgid="57055985396576230">"இதுவரை வந்த அறிவிப்புகள்"</string>
     <string name="notification_section_header_incoming" msgid="850925217908095197">"புதிது"</string>
     <string name="notification_section_header_gentle" msgid="6804099527336337197">"சைலன்ட்"</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 21a0ca9..f4d93ac 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -125,7 +125,7 @@
     <string name="screenrecord_start_error" msgid="2200660692479682368">"స్క్రీన్ రికార్డింగ్ ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
     <string name="usb_preference_title" msgid="1439924437558480718">"USB ఫైల్ బదిలీ ఎంపికలు"</string>
     <string name="use_mtp_button_title" msgid="5036082897886518086">"మీడియా ప్లేయర్‌గా (MTP) మౌంట్ చేయి"</string>
-    <string name="use_ptp_button_title" msgid="7676427598943446826">"కెమెరాగా (PTP) మౌంట్ చేయి"</string>
+    <string name="use_ptp_button_title" msgid="7676427598943446826">"ఒక కెమెరాగా (PTP) మౌంట్ చేయండి"</string>
     <string name="installer_cd_button_title" msgid="5499998592841984743">"Macకు Android ఫైల్ బదిలీ యాప్ ఇన్‌స్టాల్ చేయండి"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"వెనుకకు"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"హోమ్"</string>
@@ -147,7 +147,7 @@
     <string name="accessibility_manage_notification" msgid="582215815790143983">"నోటిఫికేషన్‌లను నిర్వహించండి"</string>
     <string name="phone_label" msgid="5715229948920451352">"ఫోన్‌ను తెరువు"</string>
     <string name="voice_assist_label" msgid="3725967093735929020">"వాయిస్ అసిస్టెంట్‌ను తెరువు"</string>
-    <string name="camera_label" msgid="8253821920931143699">"కెమెరాను తెరువు"</string>
+    <string name="camera_label" msgid="8253821920931143699">"కెమెరాను తెరవండి"</string>
     <string name="cancel" msgid="1089011503403416730">"రద్దు చేయి"</string>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"నిర్ధారించు"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"మళ్లీ ప్రయత్నించు"</string>
@@ -430,11 +430,11 @@
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
-    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరం కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
-    <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరం కెమెరా, మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
+    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
+    <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్‌లను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"మీ మైక్రోఫోన్‌ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లు, సర్వీస్‌లకు యాక్సెస్‌ను ఇది అన్‌బ్లాక్ చేస్తుంది."</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లు, సర్వీస్‌లకు యాక్సెస్‌ను ఇది అన్‌బ్లాక్ చేస్తుంది."</string>
-    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"మీ కెమెరాను లేదా మైక్రోఫోన్‌ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లు, సర్వీస్‌లకు యాక్సెస్‌ను ఇది అన్‌బ్లాక్ చేస్తుంది."</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ఇది, మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లకు, సర్వీస్‌లకు యాక్సెస్‌ను అన్‌బ్లాక్ చేస్తుంది."</string>
+    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ఇది, మీ కెమెరాను లేదా మైక్రోఫోన్‌ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్‌లకు, సర్వీస్‌లకు యాక్సెస్‌ను అన్‌బ్లాక్ చేస్తుంది."</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"ఛార్జ్ చేయబడింది"</string>
@@ -462,7 +462,7 @@
     <string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string>
     <string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ద్వారా అందించబడింది"</string>
     <string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
-    <string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ చిహ్నం నుండి స్వైప్"</string>
+    <string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ కోసం చిహ్నం నుండి స్వైప్ చేయండి"</string>
     <string name="camera_hint" msgid="4519495795000658637">"కెమెరా కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string>
     <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"మొత్తం నిశ్శబ్దం. దీని వలన స్క్రీన్ రీడర్‌లు కూడా నిశ్శబ్దమవుతాయి."</string>
     <string name="interruption_level_none" msgid="219484038314193379">"మొత్తం నిశ్శబ్దం"</string>
@@ -756,13 +756,13 @@
     <string name="notification_delegate_header" msgid="1264510071031479920">"ప్రాక్సీ చేయబడిన నోటిఫికేషన్"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"అన్ని <xliff:g id="APP_NAME">%1$s</xliff:g> నోటిఫికేషన్‌లు"</string>
     <string name="see_more_title" msgid="7409317011708185729">"మరిన్ని చూడండి"</string>
-    <string name="appops_camera" msgid="5215967620896725715">"ఈ యాప్ ఈ కెమెరాను ఉపయోగిస్తోంది."</string>
+    <string name="appops_camera" msgid="5215967620896725715">"ఈ యాప్, కెమెరాను ఉపయోగిస్తోంది."</string>
     <string name="appops_microphone" msgid="8805468338613070149">"ఈ యాప్ మైక్రోఫోన్‌ను ఉపయోగిస్తుంది."</string>
     <string name="appops_overlay" msgid="4822261562576558490">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది."</string>
-    <string name="appops_camera_mic" msgid="7032239823944420431">"ఈ యాప్ మైక్రోఫోన్ మరియు కెమెరాను ఉపయోగిస్తుంది."</string>
-    <string name="appops_camera_overlay" msgid="6466845606058816484">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది మరియు కెమెరాను ఉపయోగిస్తుంది."</string>
+    <string name="appops_camera_mic" msgid="7032239823944420431">"ఈ యాప్ మైక్రోఫోన్‌ను, కెమెరాను ఉపయోగిస్తోంది."</string>
+    <string name="appops_camera_overlay" msgid="6466845606058816484">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది, కెమెరాను ఉపయోగిస్తోంది."</string>
     <string name="appops_mic_overlay" msgid="4609326508944233061">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది మరియు మైక్రోఫోన్‌ను ఉపయోగిస్తుంది."</string>
-    <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది మరియు మైక్రోఫోన్, కెమెరాను ఉపయోగిస్తుంది."</string>
+    <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"ఈ యాప్ మీ స్క్రీన్‌లోని ఇతర యాప్‌లపై ప్రదర్శించబడుతోంది, మైక్రోఫోన్, కెమెరాను ఉపయోగిస్తోంది."</string>
     <string name="notification_appops_settings" msgid="5208974858340445174">"సెట్టింగ్‌లు"</string>
     <string name="notification_appops_ok" msgid="2177609375872784124">"సరే"</string>
     <string name="feedback_alerted" msgid="5192459808484271208">"ఈ నోటిఫికేషన్, సిస్టమ్ ద్వారా దానంతట అదే &lt;b&gt;ఆటోమేటిక్‌గా ప్రమోట్ చేయబడింది&lt;/b&gt;."</string>
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్‌వర్క్‌ల కోసం సెర్చ్ చేస్తోంది…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్‌వర్క్‌కు కనెక్ట్ చేయడం విఫలమైంది"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్‌వర్క్‌లను మార్చడానికి, ఈథర్‌నెట్‌ను డిస్‌కనెక్ట్ చేయండి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
index 7eded1e..592f8ce 100644
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ b/packages/SystemUI/res/values-te/strings_tv.xml
@@ -28,8 +28,8 @@
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"నోటిఫికేషన్‌లు లేవు"</string>
     <string name="mic_recording_announcement" msgid="7587123608060316575">"మైక్రోఫోన్ రికార్డింగ్ చేస్తోంది"</string>
     <string name="camera_recording_announcement" msgid="7240177719403759112">"కెమెరా రికార్డింగ్ చేస్తోంది"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్ రికార్డింగ్ చేస్తున్నాయి"</string>
+    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్‌లు రికార్డింగ్ చేస్తున్నాయి"</string>
     <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేసింది"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"కెమెరా రికార్డింగ్ చేయడం ఆపివేసింది"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
+    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"రికార్డింగ్ చేయడాన్ని కెమెరా ఆపివేసింది"</string>
+    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్‌లు రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e053541..d0603a9 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9eb1449..bb1cbe8 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d1599bf..15b702c 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 482742a..dc30e0b 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1188,4 +1188,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9c80c7e..667a957 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3b77577..b02ef40 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2d8d2de..d046494e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 41ebca9..c7747fa 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 63bc3f6..e7ffeea 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index eeb7522..b49cd2b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 29ae369..d4fb4898 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1176,4 +1176,5 @@
     <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
     <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
+    <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c432395..7014926 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -87,7 +87,7 @@
     <dimen name="status_bar_left_clock_starting_padding">0dp</dimen>
 
     <!-- End padding for left-aligned status bar clock -->
-    <dimen name="status_bar_left_clock_end_padding">7dp</dimen>
+    <dimen name="status_bar_left_clock_end_padding">2dp</dimen>
 
     <!-- Spacing after the wifi signals that is present if there are any icons following it. -->
     <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen>
@@ -750,9 +750,7 @@
     <!-- The margin between the status view and the notifications on Keyguard.-->
     <dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
     <!-- Minimum margin between clock and status bar -->
-    <dimen name="keyguard_clock_top_margin">36dp</dimen>
-    <!-- The margin between top of clock and bottom of lock icon. -->
-    <dimen name="keyguard_clock_lock_margin">16dp</dimen>
+    <dimen name="keyguard_clock_top_margin">18dp</dimen>
     <!-- The amount to shift the clocks during a small/large transition -->
     <dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
     <!-- When large clock is showing, offset the smartspace by this amount -->
@@ -1157,9 +1155,9 @@
     <dimen name="default_burn_in_prevention_offset">15dp</dimen>
 
     <!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
-         direction that elements aer moved to prevent burn-in on AOD-->
-    <dimen name="udfps_burn_in_offset_x">2dp</dimen>
-    <dimen name="udfps_burn_in_offset_y">8dp</dimen>
+         direction that elements are moved to prevent burn-in on AOD-->
+    <dimen name="udfps_burn_in_offset_x">7px</dimen>
+    <dimen name="udfps_burn_in_offset_y">28px</dimen>
 
     <dimen name="corner_size">8dp</dimen>
     <dimen name="top_padding">0dp</dimen>
@@ -1281,7 +1279,7 @@
 
     <dimen name="ongoing_appops_dialog_circle_size">32dp</dimen>
 
-    <dimen name="ongoing_appops_dialog_icon_size">20dp</dimen>
+    <dimen name="ongoing_appops_dialog_icon_size">16dp</dimen>
 
     <dimen name="ongoing_appops_dialog_side_padding">16dp</dimen>
 
@@ -1312,6 +1310,7 @@
     <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
     <dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
     <dimen name="qs_aa_media_rec_album_size_expanded">76dp</dimen>
+    <dimen name="qs_aa_media_gradient_bg_width">32dp</dimen>
     <dimen name="qs_aa_media_rec_album_margin">8dp</dimen>
     <dimen name="qs_aa_media_rec_album_margin_vert">4dp</dimen>
     <dimen name="qq_aa_media_rec_header_text_size">16sp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 03ba28a..c26de37 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3020,6 +3020,10 @@
     <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
     <!-- Provider Model: Failure notification for connect -->
     <string name="wifi_failed_connect_message">Failed to connect to network</string>
+    <!-- Provider Model: Toast message for when the user selects cellular as the internet provider and Wi-Fi auto-connect is temporarily disabled [CHAR LIMIT=60]-->
+    <string name="wifi_wont_autoconnect_for_now">Wi\u2011Fi won\u2019t auto-connect for now</string>
     <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
     <string name="see_all_networks">See all</string>
+    <!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
+    <string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d254742..93d60cc 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -416,7 +416,9 @@
     </style>
 
     <!-- Overridden by values-television/styles.xml with tv-specific settings -->
-    <style name="volume_dialog_theme" parent="Theme.SystemUI"/>
+    <style name="volume_dialog_theme" parent="Theme.SystemUI">
+        <item name="android:windowIsFloating">true</item>
+    </style>
 
     <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog" />
 
@@ -951,4 +953,60 @@
         <item name="android:maxHeight">4dp</item>
     </style>
 
+    <!-- Internet Dialog -->
+    <style name="InternetDialog">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_vertical|start</item>
+        <item name="android:layout_marginStart">@dimen/internet_dialog_network_layout_margin</item>
+    </style>
+
+    <style name="InternetDialog.Network">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">88dp</item>
+        <item name="android:layout_marginEnd">@dimen/internet_dialog_network_layout_margin</item>
+        <item name="android:paddingStart">22dp</item>
+        <item name="android:paddingEnd">22dp</item>
+        <item name="android:orientation">horizontal</item>
+        <item name="android:focusable">true</item>
+        <item name="android:clickable">true</item>
+    </style>
+
+    <style name="InternetDialog.NetworkTitle">
+        <item name="android:layout_marginEnd">7dp</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:textAppearance">@style/TextAppearance.InternetDialog</item>
+    </style>
+
+    <style name="InternetDialog.NetworkTitle.Active">
+        <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
+    </style>
+
+    <style name="InternetDialog.NetworkSummary">
+        <item name="android:layout_marginEnd">34dp</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary</item>
+    </style>
+
+    <style name="InternetDialog.NetworkSummary.Active">
+        <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
+        </item>
+    </style>
+
+    <style name="TextAppearance.InternetDialog">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textDirection">locale</item>
+    </style>
+
+    <style name="TextAppearance.InternetDialog.Secondary">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="TextAppearance.InternetDialog.Active"/>
+
+    <style name="TextAppearance.InternetDialog.Secondary.Active"/>
+
 </resources>
diff --git a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
index 5c41ad8..b6258d1 100644
--- a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
@@ -19,25 +19,6 @@
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <Constraint
-        android:id="@+id/recommendation_card_icon"
-        android:layout_width="@dimen/qs_media_icon_size"
-        android:layout_height="@dimen/qs_media_icon_size"
-        android:layout_marginTop="@dimen/qs_media_padding"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
-        app:layout_constraintHorizontal_bias="0" />
-
-    <Constraint
-        android:id="@+id/recommendation_card_text"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
-        app:layout_constraintHorizontal_bias="0" />
-
-    <Constraint
         android:id="@+id/media_cover1_container"
         android:layout_width="0dp"
         android:layout_height="@dimen/qs_aa_media_rec_album_size_collapsed"
diff --git a/packages/SystemUI/res/xml/media_recommendation_expanded.xml b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
index 8a3d5ca..2fb3341 100644
--- a/packages/SystemUI/res/xml/media_recommendation_expanded.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
@@ -19,25 +19,6 @@
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <Constraint
-        android:id="@+id/recommendation_card_icon"
-        android:layout_width="@dimen/qs_media_icon_size"
-        android:layout_height="@dimen/qs_media_icon_size"
-        android:layout_marginTop="@dimen/qs_media_padding"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
-        app:layout_constraintHorizontal_bias="0" />
-
-    <Constraint
-        android:id="@+id/recommendation_card_text"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
-        app:layout_constraintHorizontal_bias="0" />
-
-    <Constraint
         android:id="@+id/media_cover1_container"
         android:layout_width="0dp"
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index e33985d..6154d84 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,16 +18,19 @@
 
 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
+import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.util.DisplayMetrics;
 import android.view.Surface;
+import android.view.WindowManager;
 
 /* Common code */
 public class Utilities {
@@ -117,21 +120,20 @@
         return hints;
     }
 
-    /** See {@link #isTablet(Configuration, Context)} */
+    /** @return whether or not {@param context} represents that of a large screen device or not */
+    @TargetApi(Build.VERSION_CODES.R)
     public static boolean isTablet(Context context) {
-        Configuration newConfig = context.getResources().getConfiguration();
-        return isTablet(newConfig, context);
+        final WindowManager windowManager = context.getSystemService(WindowManager.class);
+        final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
+
+        float originalSmallestWidth = dpiFromPx(Math.min(bounds.width(), bounds.height()),
+                context.getResources().getConfiguration().densityDpi);
+        return dpiFromPx(Math.min(bounds.width(), bounds.height()), DENSITY_DEVICE_STABLE)
+                >= TABLET_MIN_DPS && originalSmallestWidth >= TABLET_MIN_DPS;
     }
 
-    /**
-     * @return whether or not {@param newConfig} represents that of a large screen device or not
-     */
-    public static boolean isTablet(Configuration newConfig, Context context) {
-        float density = Resources.getSystem().getDisplayMetrics().density;
-        int size = Math.min((int) (density * newConfig.screenWidthDp),
-                (int) (density* newConfig.screenHeightDp));
-        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
-        return (size / densityRatio) >= TABLET_MIN_DPS;
+    public static float dpiFromPx(float size, int densityDpi) {
+        float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+        return (size / densityRatio);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 92f89d6..efcf40a 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -160,9 +160,7 @@
         mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mBatteryController.removeCallback(mBatteryCallback);
-        if (!mView.isAttachedToWindow()) {
-            mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
-        }
+        mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
     }
 
     /** Animate the clock appearance */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 11c4b6a..260b393 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -72,13 +72,16 @@
      * Clock for both small and large sizes
      */
     private AnimatableClockController mClockViewController;
-    private FrameLayout mClockFrame;
+    private FrameLayout mClockFrame; // top aligned clock
     private AnimatableClockController mLargeClockViewController;
-    private FrameLayout mLargeClockFrame;
+    private FrameLayout mLargeClockFrame; // centered clock
 
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardBypassController mBypassController;
 
+    private int mLargeClockTopMargin = 0;
+    private int mKeyguardClockTopMargin = 0;
+
     /**
      * Listener for changes to the color palette.
      *
@@ -177,6 +180,8 @@
         }
         mColorExtractor.addOnColorsChangedListener(mColorsListener);
         mView.updateColors(getGradientColors());
+        mKeyguardClockTopMargin =
+                mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
 
         if (mOnlyClock) {
             View ksa = mView.findViewById(R.id.keyguard_status_area);
@@ -241,6 +246,8 @@
      */
     public void onDensityOrFontScaleChanged() {
         mView.onDensityOrFontScaleChanged();
+        mKeyguardClockTopMargin =
+                mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
 
         updateClockLayout();
     }
@@ -249,9 +256,12 @@
         if (mSmartspaceController.isEnabled()) {
             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
                     MATCH_PARENT);
-            lp.topMargin = getContext().getResources().getDimensionPixelSize(
+            mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
                     R.dimen.keyguard_large_clock_top_margin);
+            lp.topMargin = mLargeClockTopMargin;
             mLargeClockFrame.setLayoutParams(lp);
+        } else {
+            mLargeClockTopMargin = 0;
         }
     }
 
@@ -363,6 +373,28 @@
         }
     }
 
+    /**
+     * Get y-bottom position of the currently visible clock on the keyguard.
+     * We can't directly getBottom() because clock changes positions in AOD for burn-in
+     */
+    int getClockBottom(int statusBarHeaderHeight) {
+        if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
+            View clock = mLargeClockFrame.findViewById(
+                    com.android.systemui.R.id.animatable_clock_view_large);
+            int frameHeight = mLargeClockFrame.getHeight();
+            int clockHeight = clock.getHeight();
+            return frameHeight / 2 + clockHeight / 2;
+        } else {
+            return mClockFrame.findViewById(
+                    com.android.systemui.R.id.animatable_clock_view).getHeight()
+                    + statusBarHeaderHeight + mKeyguardClockTopMargin;
+        }
+    }
+
+    boolean isClockTopAligned() {
+        return mLargeClockFrame.getVisibility() != View.VISIBLE;
+    }
+
     private void updateAodIcons() {
         NotificationIconContainer nic = (NotificationIconContainer)
                 mView.findViewById(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index a35aedf..1862fc7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -102,7 +102,7 @@
 
         ConstraintSet cs = new ConstraintSet();
         cs.clone(mContainer);
-        cs.setGuidelinePercent(R.id.pin_pad_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
+        cs.setGuidelinePercent(R.id.pattern_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
                 ? halfOpenPercentage : 0.0f);
         cs.applyTo(mContainer);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 64d214d..d80a408 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,8 +21,7 @@
 
 import static java.lang.Integer.max;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
@@ -40,6 +39,8 @@
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import androidx.annotation.VisibleForTesting;
@@ -55,7 +56,6 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -85,6 +85,13 @@
 
     private static final long IME_DISAPPEAR_DURATION_MS = 125;
 
+    // The duration of the animation to switch bouncer sides.
+    private static final long BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS = 500;
+
+    // How much of the switch sides animation should be dedicated to fading the bouncer out. The
+    // remainder will fade it back in again.
+    private static final float BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
+
     @VisibleForTesting
     KeyguardSecurityViewFlipper mSecurityViewFlipper;
     private AlertDialog mAlertDialog;
@@ -322,18 +329,87 @@
                 ? 0 : (int) (getMeasuredWidth() - mSecurityViewFlipper.getWidth());
 
         if (animate) {
-            mRunningOneHandedAnimator =
-                    mSecurityViewFlipper.animate().translationX(targetTranslation);
-            mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-            mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mRunningOneHandedAnimator = null;
+            // This animation is a bit fun to implement. The bouncer needs to move, and fade in/out
+            // at the same time. The issue is, the bouncer should only move a short amount (120dp or
+            // so), but obviously needs to go from one side of the screen to the other. This needs a
+            // pretty custom animation.
+            //
+            // This works as follows. It uses a ValueAnimation to simply drive the animation
+            // progress. This animator is responsible for both the translation of the bouncer, and
+            // the current fade. It will fade the bouncer out while also moving it along the 120dp
+            // path. Once the bouncer is fully faded out though, it will "snap" the bouncer closer
+            // to its destination, then fade it back in again. The effect is that the bouncer will
+            // move from 0 -> X while fading out, then (destination - X) -> destination while fading
+            // back in again.
+            // TODO(b/195012405): Make this animation properly abortable.
+            Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
+                    mContext, android.R.interpolator.fast_out_extra_slow_in);
+            Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
+            Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+
+            ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f);
+            anim.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
+            anim.setInterpolator(Interpolators.LINEAR);
+
+            int initialTranslation = (int) mSecurityViewFlipper.getTranslationX();
+            int totalTranslation = (int) getResources().getDimension(
+                    R.dimen.one_handed_bouncer_move_animation_translation);
+
+            final boolean shouldRestoreLayerType = mSecurityViewFlipper.hasOverlappingRendering()
+                    && mSecurityViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
+            if (shouldRestoreLayerType) {
+                mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
+            }
+
+            anim.addUpdateListener(animation -> {
+                float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
+                boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
+
+                int currentTranslation = (int) (positionInterpolator.getInterpolation(
+                        animation.getAnimatedFraction()) * totalTranslation);
+                int translationRemaining = totalTranslation - currentTranslation;
+
+                // Flip the sign if we're going from right to left.
+                if (mIsSecurityViewLeftAligned) {
+                    currentTranslation = -currentTranslation;
+                    translationRemaining = -translationRemaining;
+                }
+
+                if (isFadingOut) {
+                    // The bouncer fades out over the first X%.
+                    float fadeOutFraction = MathUtils.constrainedMap(
+                            /* rangeMin= */0.0f,
+                            /* rangeMax= */1.0f,
+                            /* valueMin= */0.0f,
+                            /* valueMax= */switchPoint,
+                            animation.getAnimatedFraction());
+                    float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
+                    mSecurityViewFlipper.setAlpha(1f - opacity);
+
+                    // Animate away from the source.
+                    mSecurityViewFlipper.setTranslationX(initialTranslation + currentTranslation);
+                } else {
+                    // And in again over the remaining (100-X)%.
+                    float fadeInFraction = MathUtils.constrainedMap(
+                            /* rangeMin= */0.0f,
+                            /* rangeMax= */1.0f,
+                            /* valueMin= */switchPoint,
+                            /* valueMax= */1.0f,
+                            animation.getAnimatedFraction());
+
+                    float opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
+                    mSecurityViewFlipper.setAlpha(opacity);
+
+                    // Fading back in, animate towards the destination.
+                    mSecurityViewFlipper.setTranslationX(targetTranslation - translationRemaining);
+                }
+
+                if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
+                    mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
                 }
             });
 
-            mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mRunningOneHandedAnimator.start();
+            anim.start();
         } else {
             mSecurityViewFlipper.setTranslationX(targetTranslation);
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8bf8e09..d23b1c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,6 +187,20 @@
     }
 
     /**
+     * Get y-bottom position of the currently visible clock.
+     */
+    public int getClockBottom(int statusBarHeaderHeight) {
+        return mKeyguardClockSwitchController.getClockBottom(statusBarHeaderHeight);
+    }
+
+    /**
+     * @return true if the currently displayed clock is top aligned (as opposed to center aligned)
+     */
+    public boolean isClockTopAligned() {
+        return mKeyguardClockSwitchController.isClockTopAligned();
+    }
+
+    /**
      * Set whether the view accessibility importance mode.
      */
     public void setStatusAccessibilityImportance(int mode) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index dd3bb89..ef4353b 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -45,9 +45,10 @@
     private int mRadius;
 
     private ImageView mLockIcon;
-    private ImageView mUnlockBgView;
+    private ImageView mBgView;
 
     private int mLockIconColor;
+    private boolean mUseBackground = false;
 
     public LockIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,19 +59,19 @@
     public void onFinishInflate() {
         super.onFinishInflate();
         mLockIcon = findViewById(R.id.lock_icon);
-        mUnlockBgView = findViewById(R.id.lock_icon_bg);
+        mBgView = findViewById(R.id.lock_icon_bg);
     }
 
-    void updateColorAndBackgroundVisibility(boolean useBackground) {
-        if (useBackground) {
+    void updateColorAndBackgroundVisibility() {
+        if (mUseBackground && mLockIcon.getDrawable() != null) {
             mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
                     android.R.attr.textColorPrimary);
-            mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
-            mUnlockBgView.setVisibility(View.VISIBLE);
+            mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+            mBgView.setVisibility(View.VISIBLE);
         } else {
             mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
                     R.attr.wallpaperTextColorAccent);
-            mUnlockBgView.setVisibility(View.GONE);
+            mBgView.setVisibility(View.GONE);
         }
 
         mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
@@ -78,6 +79,22 @@
 
     void setImageDrawable(Drawable drawable) {
         mLockIcon.setImageDrawable(drawable);
+
+        if (!mUseBackground) return;
+
+        if (drawable == null) {
+            mBgView.setVisibility(View.INVISIBLE);
+        } else {
+            mBgView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * Whether or not to render the lock icon background. Mainly used for UDPFS.
+     */
+    public void setUseBackground(boolean useBackground) {
+        mUseBackground = useBackground;
+        updateColorAndBackgroundVisibility();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index a115195..8cfd225 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -19,6 +19,8 @@
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 
 import static com.android.systemui.classifier.Classifier.LOCK_ICON;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -33,6 +35,7 @@
 import android.os.Process;
 import android.os.Vibrator;
 import android.util.DisplayMetrics;
+import android.util.MathUtils;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.MotionEvent;
@@ -60,6 +63,8 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import com.airbnb.lottie.LottieAnimationView;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -95,6 +100,8 @@
     @NonNull private final DelayableExecutor mExecutor;
     private boolean mUdfpsEnrolled;
 
+    @NonNull private LottieAnimationView mAodFp;
+
     @NonNull private final AnimatedVectorDrawable mFpToUnlockIcon;
     @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
     @NonNull private final Drawable mLockIcon;
@@ -113,6 +120,7 @@
     private boolean mIsKeyguardShowing;
     private boolean mUserUnlockedWithBiometric;
     private Runnable mCancelDelayedUpdateVisibilityRunnable;
+    private Runnable mOnGestureDetectedRunnable;
 
     private boolean mUdfpsSupported;
     private float mHeightPixels;
@@ -122,6 +130,12 @@
     private boolean mShowUnlockIcon;
     private boolean mShowLockIcon;
 
+    // for udfps when strong auth is required or unlocked on AOD
+    private boolean mShowAODFpIcon;
+    private final int mMaxBurnInOffsetX;
+    private final int mMaxBurnInOffsetY;
+    private float mInterpolatedDarkAmount;
+
     private boolean mDownDetected;
     private boolean mDetectedLongPress;
     private final Rect mSensorTouchLocation = new Rect();
@@ -156,6 +170,12 @@
         mAuthRippleController = authRippleController;
 
         final Context context = view.getContext();
+        mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
+        mMaxBurnInOffsetX = context.getResources()
+                .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
+        mMaxBurnInOffsetY = context.getResources()
+                .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
+
         mUnlockIcon = mView.getContext().getResources().getDrawable(
             R.drawable.ic_unlock,
             mView.getContext().getTheme());
@@ -174,19 +194,19 @@
 
     @Override
     protected void onInit() {
-        mAuthController.addCallback(mAuthControllerCallback);
-        mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
-
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
     @Override
     protected void onViewAttached() {
+        updateIsUdfpsEnrolled();
         updateConfiguration();
         updateKeyguardShowing();
         mUserUnlockedWithBiometric = false;
+
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
         mIsDozing = mStatusBarStateController.isDozing();
+        mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
         mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
         mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
         mStatusBarState = mStatusBarStateController.getState();
@@ -194,15 +214,18 @@
         updateColors();
         mConfigurationController.addCallback(mConfigurationListener);
 
+        mAuthController.addCallback(mAuthControllerCallback);
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mKeyguardStateController.addCallback(mKeyguardStateCallback);
         mDownDetected = false;
+        updateBurnInOffsets();
         updateVisibility();
     }
 
     @Override
     protected void onViewDetached() {
+        mAuthController.removeCallback(mAuthControllerCallback);
         mConfigurationController.removeCallback(mConfigurationListener);
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -232,7 +255,7 @@
             mCancelDelayedUpdateVisibilityRunnable = null;
         }
 
-        if (!mIsKeyguardShowing) {
+        if (!mIsKeyguardShowing && !mIsDozing) {
             mView.setVisibility(View.INVISIBLE);
             return;
         }
@@ -243,6 +266,7 @@
         mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
             && (!mUdfpsEnrolled || !mRunningFPS);
         mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
+        mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS;
 
         final CharSequence prevContentDescription = mView.getContentDescription();
         if (mShowLockIcon) {
@@ -265,10 +289,22 @@
             }
             mView.setVisibility(View.VISIBLE);
             mView.setContentDescription(mUnlockedLabel);
+        } else if (mShowAODFpIcon) {
+            mView.setImageDrawable(null);
+            mView.setContentDescription(null);
+            mAodFp.setVisibility(View.VISIBLE);
+            mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
+            mView.setVisibility(View.VISIBLE);
         } else {
             mView.setVisibility(View.INVISIBLE);
             mView.setContentDescription(null);
         }
+
+        if (!mShowAODFpIcon) {
+            mAodFp.setVisibility(View.INVISIBLE);
+            mAodFp.setContentDescription(null);
+        }
+
         if (!Objects.equals(prevContentDescription, mView.getContentDescription())
                 && mView.getContentDescription() != null) {
             mView.announceForAccessibility(mView.getContentDescription());
@@ -310,7 +346,7 @@
     }
 
     private void updateColors() {
-        mView.updateColorAndBackgroundVisibility(mUdfpsSupported);
+        mView.updateColorAndBackgroundVisibility();
     }
 
     private void updateConfiguration() {
@@ -346,10 +382,12 @@
 
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("mUdfpsSupported: " + mUdfpsSupported);
         pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
         pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
         pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println(" mShowLockIcon: " + mShowLockIcon);
+        pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
         pw.println("  mIsDozing: " + mIsDozing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
         pw.println("  mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -357,17 +395,59 @@
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println("  mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
         pw.println("  mQsExpanded: " + mQsExpanded);
+        pw.println("  mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
 
         if (mView != null) {
             mView.dump(fd, pw, args);
         }
     }
 
+    /** Every minute, update the aod icon's burn in offset */
+    public void dozeTimeTick() {
+        updateBurnInOffsets();
+    }
+
+    private void updateBurnInOffsets() {
+        float offsetX = MathUtils.lerp(0f,
+                getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
+                        - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
+        float offsetY = MathUtils.lerp(0f,
+                getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
+                        - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
+        float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
+
+        mAodFp.setTranslationX(offsetX);
+        mAodFp.setTranslationY(offsetY);
+        mAodFp.setProgress(progress);
+        mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
+    }
+
+    private void updateIsUdfpsEnrolled() {
+        boolean wasUdfpsSupported = mUdfpsSupported;
+        boolean wasUdfpsEnrolled = mUdfpsEnrolled;
+
+        mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+        mView.setUseBackground(mUdfpsSupported);
+
+        mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
+        if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
+            updateVisibility();
+        }
+    }
+
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
+                public void onDozeAmountChanged(float linear, float eased) {
+                    mInterpolatedDarkAmount = eased;
+                    updateBurnInOffsets();
+                }
+
+                @Override
                 public void onDozingChanged(boolean isDozing) {
                     mIsDozing = isDozing;
+                    updateBurnInOffsets();
+                    updateIsUdfpsEnrolled();
                     updateVisibility();
                 }
 
@@ -441,7 +521,7 @@
                     mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
                         KeyguardUpdateMonitor.getCurrentUser());
             }
-            mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
+            updateIsUdfpsEnrolled();
             updateVisibility();
         }
 
@@ -487,8 +567,7 @@
 
                     // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or
                     // MotionEvent.ACTION_UP (see #onTouchEvent)
-                    mDownDetected = true;
-                    if (mVibrator != null) {
+                    if (mVibrator != null && !mDownDetected) {
                         mVibrator.vibrate(
                                 Process.myUid(),
                                 getContext().getOpPackageName(),
@@ -496,6 +575,8 @@
                                 "lockIcon-onDown",
                                 VIBRATION_SONIFICATION_ATTRIBUTES);
                     }
+
+                    mDownDetected = true;
                     return true;
                 }
 
@@ -553,6 +634,9 @@
                         mAuthRippleController.showRipple(FINGERPRINT);
                     }
                     updateVisibility();
+                    if (mOnGestureDetectedRunnable != null) {
+                        mOnGestureDetectedRunnable.run();
+                    }
                     mKeyguardViewController.showBouncer(/* scrim */ true);
                     return true;
                 }
@@ -563,16 +647,18 @@
      * in a 'clickable' state
      * @return whether to intercept the touch event
      */
-    public boolean onTouchEvent(MotionEvent event) {
+    public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
         if (mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
-                && mView.getVisibility() == View.VISIBLE) {
+                && (mView.getVisibility() == View.VISIBLE
+                || mAodFp.getVisibility() == View.VISIBLE)) {
+            mOnGestureDetectedRunnable = onGestureDetectedRunnable;
             mGestureDetector.onTouchEvent(event);
         }
 
         // we continue to intercept all following touches until we see MotionEvent.ACTION_CANCEL UP
         // or MotionEvent.ACTION_UP. this is to avoid passing the touch to NPV
         // after the lock icon disappears on device entry
-        if (mDownDetected && mDetectedLongPress) {
+        if (mDownDetected) {
             if (event.getAction() == MotionEvent.ACTION_CANCEL
                     || event.getAction() == MotionEvent.ACTION_UP) {
                 mDownDetected = false;
@@ -596,7 +682,7 @@
     private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
         @Override
         public void onAllAuthenticatorsRegistered() {
-            mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+            updateIsUdfpsEnrolled();
             updateConfiguration();
         }
     };
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
index 3a0357d..4331f52 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
@@ -16,7 +16,8 @@
 
 package com.android.keyguard.dagger;
 
-import com.android.systemui.statusbar.phone.UserAvatarView;
+import android.widget.FrameLayout;
+
 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
 
 import dagger.BindsInstance;
@@ -31,8 +32,7 @@
     /** Simple factory for {@link KeyguardUserSwitcherComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        KeyguardQsUserSwitchComponent build(
-                @BindsInstance UserAvatarView userAvatarView);
+        KeyguardQsUserSwitchComponent build(@BindsInstance FrameLayout userAvatarContainer);
     }
 
     /** Builds a {@link KeyguardQsUserSwitchController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 05256e6..f182e77 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Prefs;
+import com.android.systemui.shared.system.SysUiStatsLog;
 
 /**
  * Contains logic for an accessibility floating menu view.
@@ -177,6 +178,9 @@
     }
 
     private void onDragEnd(Position position) {
+        SysUiStatsLog.write(SysUiStatsLog.ACCESSIBILITY_FLOATING_MENU_UI_CHANGED,
+                position.getPercentageX(), position.getPercentageY(),
+                mContext.getResources().getConfiguration().orientation);
         savePosition(mContext, position);
         showDockTooltipIfNecessary(mContext);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 17818cd..59d9aff 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -19,8 +19,9 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.util.MathUtils.constrain;
 import static android.util.MathUtils.sq;
+import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.systemBars;
 
 import static java.util.Objects.requireNonNull;
 
@@ -41,7 +42,6 @@
 import android.graphics.drawable.LayerDrawable;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -95,7 +95,6 @@
     private boolean mIsShowing;
     private boolean mIsDownInEnlargedTouchArea;
     private boolean mIsDragging = false;
-    private boolean mImeVisibility;
     @Alignment
     private int mAlignment;
     @SizeType
@@ -108,8 +107,10 @@
     private int mRadiusType;
     private int mMargin;
     private int mPadding;
-    private int mScreenHeight;
-    private int mScreenWidth;
+    // The display width excludes the window insets of the system bar and display cutout.
+    private int mDisplayHeight;
+    // The display Height excludes the window insets of the system bar and display cutout.
+    private int mDisplayWidth;
     private int mIconWidth;
     private int mIconHeight;
     private int mInset;
@@ -118,6 +119,8 @@
     private int mRelativeToPointerDownX;
     private int mRelativeToPointerDownY;
     private float mRadius;
+    private final Rect mDisplayInsetsRect = new Rect();
+    private final Rect mImeInsetsRect = new Rect();
     private final Position mPosition;
     private float mSquareScaledTouchSlop;
     private final Configuration mLastConfiguration;
@@ -506,9 +509,21 @@
     }
 
     private WindowInsets onWindowInsetsApplied(WindowInsets insets) {
-        final boolean currentImeVisibility = insets.isVisible(ime());
-        if (currentImeVisibility != mImeVisibility) {
-            mImeVisibility = currentImeVisibility;
+        final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+        final Rect displayWindowInsetsRect = getDisplayInsets(windowMetrics).toRect();
+        if (!displayWindowInsetsRect.equals(mDisplayInsetsRect)) {
+            updateDisplaySizeWith(windowMetrics);
+            updateLocationWith(mPosition);
+        }
+
+        final Rect imeInsetsRect = windowMetrics.getWindowInsets().getInsets(ime()).toRect();
+        if (!imeInsetsRect.equals(mImeInsetsRect)) {
+            if (isImeVisible(imeInsetsRect)) {
+                mImeInsetsRect.set(imeInsetsRect);
+            } else {
+                mImeInsetsRect.setEmpty();
+            }
+
             updateLocationWith(mPosition);
         }
 
@@ -520,6 +535,11 @@
                 || (side == Alignment.LEFT && downX > currentRawX);
     }
 
+    private boolean isImeVisible(Rect imeInsetsRect) {
+        return imeInsetsRect.left != 0 || imeInsetsRect.top != 0 || imeInsetsRect.right != 0
+                || imeInsetsRect.bottom != 0;
+    }
+
     private boolean hasExceededTouchSlop(int startX, int startY, int endX, int endY) {
         return (sq(endX - startX) + sq(endY - startY)) > mSquareScaledTouchSlop;
     }
@@ -546,9 +566,9 @@
 
     private void updateDimensions() {
         final Resources res = getResources();
-        final DisplayMetrics dm = res.getDisplayMetrics();
-        mScreenWidth = dm.widthPixels;
-        mScreenHeight = dm.heightPixels;
+
+        updateDisplaySizeWith(mWindowManager.getCurrentWindowMetrics());
+
         mMargin =
                 res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_margin);
         mInset =
@@ -560,6 +580,15 @@
         updateItemViewDimensionsWith(mSizeType);
     }
 
+    private void updateDisplaySizeWith(WindowMetrics metrics) {
+        final Rect displayBounds = metrics.getBounds();
+        final Insets displayInsets = getDisplayInsets(metrics);
+        mDisplayInsetsRect.set(displayInsets.toRect());
+        displayBounds.inset(displayInsets);
+        mDisplayWidth = displayBounds.width();
+        mDisplayHeight = displayBounds.height();
+    }
+
     private void updateItemViewDimensionsWith(@SizeType int sizeType) {
         final Resources res = getResources();
         final int paddingResId =
@@ -684,11 +713,11 @@
     }
 
     private int getMaxWindowX() {
-        return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
+        return mDisplayWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
     }
 
     private int getMaxWindowY() {
-        return mScreenHeight - getWindowHeight();
+        return mDisplayHeight - getWindowHeight();
     }
 
     private InstantInsetLayerDrawable getMenuLayerDrawable() {
@@ -699,8 +728,13 @@
         return (GradientDrawable) getMenuLayerDrawable().getDrawable(INDEX_MENU_ITEM);
     }
 
+    private Insets getDisplayInsets(WindowMetrics metrics) {
+        return metrics.getWindowInsets().getInsetsIgnoringVisibility(
+                systemBars() | displayCutout());
+    }
+
     /**
-     * Updates the floating menu to be fixed at the side of the screen.
+     * Updates the floating menu to be fixed at the side of the display.
      */
     private void updateLocationWith(Position position) {
         final @Alignment int alignment = transformToAlignment(position.getPercentageX());
@@ -716,15 +750,9 @@
      * @return the moving interval if they overlap each other, otherwise 0.
      */
     private int getInterval() {
-        if (!mImeVisibility) {
-            return 0;
-        }
-
-        final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
-        final Insets imeInsets = windowMetrics.getWindowInsets().getInsets(
-                ime() | navigationBars());
-        final int imeY = mScreenHeight - imeInsets.bottom;
-        final int layoutBottomY = mCurrentLayoutParams.y + getWindowHeight();
+        final int currentLayoutY = (int) (mPosition.getPercentageY() * getMaxWindowY());
+        final int imeY = mDisplayHeight - mImeInsetsRect.bottom;
+        final int layoutBottomY = currentLayoutY + getWindowHeight();
 
         return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0;
     }
@@ -855,11 +883,12 @@
 
     @VisibleForTesting
     Rect getAvailableBounds() {
-        return new Rect(0, 0, mScreenWidth - getWindowWidth(), mScreenHeight - getWindowHeight());
+        return new Rect(0, 0, mDisplayWidth - getWindowWidth(),
+                mDisplayHeight - getWindowHeight());
     }
 
     private int getMaxLayoutHeight() {
-        return mScreenHeight - mMargin * 2;
+        return mDisplayHeight - mMargin * 2;
     }
 
     private int getLayoutWidth() {
@@ -875,7 +904,7 @@
     }
 
     private int getWindowHeight() {
-        return Math.min(mScreenHeight, mMargin * 2 + getLayoutHeight());
+        return Math.min(mDisplayHeight, mMargin * 2 + getLayoutHeight());
     }
 
     private void setSystemGestureExclusion() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
index 947466f..adb10f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 
 include /services/core/java/com/android/server/biometrics/OWNERS
+beverlyt@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 41c7ebe..b7398d8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -17,6 +17,8 @@
 
 import android.content.Context
 import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
 import android.graphics.Rect
 import android.hardware.biometrics.BiometricOverlayConstants
 import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
@@ -34,6 +36,8 @@
 import android.view.WindowManager
 import androidx.annotation.RawRes
 import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.model.KeyPath
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
@@ -140,6 +144,7 @@
                 windowManager.updateViewLayout(overlayView, overlayViewParams)
             }
         }
+        lottie.addOverlayDynamicColor(context)
 
         return view
     }
@@ -194,3 +199,21 @@
 
 private fun Display.isPortrait(): Boolean =
     rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+
+private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
+    fun update() {
+        val c = context.getColor(R.color.biometric_dialog_accent)
+        for (key in listOf(".blue600", ".blue400")) {
+            addValueCallback(
+                KeyPath(key, "**"),
+                LottieProperty.COLOR_FILTER
+            ) { PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP) }
+        }
+    }
+
+    if (composition != null) {
+        update()
+    } else {
+        addLottieOnCompositionLoadedListener { update() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 63fb8e2..594a642 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -41,7 +41,6 @@
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
 import android.media.AudioAttributes;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -65,7 +64,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -73,6 +71,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -95,7 +94,7 @@
  * controls/manages all UDFPS sensors. In other words, a single controller is registered with
  * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
  * as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or
- * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
+ * {@link IUdfpsOverlayController#showUdfpsOverlay(int)} should all have
  * {@code sensorId} parameters.
  */
 @SuppressWarnings("deprecation")
@@ -119,9 +118,7 @@
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final DumpManager mDumpManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
     @Nullable private final Vibrator mVibrator;
-    @NonNull private final Handler mMainHandler;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
@@ -131,6 +128,8 @@
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
+    @NonNull private final UnlockedScreenOffAnimationController
+            mUnlockedScreenOffAnimationController;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -245,7 +244,7 @@
                 final UdfpsEnrollHelper enrollHelper;
                 if (reason == BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
                         || reason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING) {
-                    enrollHelper = new UdfpsEnrollHelper(mContext, reason);
+                    enrollHelper = new UdfpsEnrollHelper(mContext, mFingerprintManager, reason);
                 } else {
                     enrollHelper = null;
                 }
@@ -519,7 +518,6 @@
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
@@ -533,11 +531,11 @@
             @NonNull DisplayManager displayManager,
             @Main Handler mainHandler,
             @NonNull ConfigurationController configurationController,
-            @NonNull SystemClock systemClock) {
+            @NonNull SystemClock systemClock,
+            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mContext = context;
         mExecution = execution;
         // TODO (b/185124905): inject main handler and vibrator once done prototyping
-        mMainHandler = new Handler(Looper.getMainLooper());
         mVibrator = vibrator;
         mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -551,7 +549,6 @@
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mDumpManager = dumpManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mKeyguardViewMediator = keyguardViewMediator;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
@@ -562,6 +559,7 @@
         mKeyguardBypassController = keyguardBypassController;
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
+        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -661,6 +659,20 @@
         }
     }
 
+    private boolean shouldRotate(@Nullable UdfpsAnimationViewController animation) {
+        if (!(animation instanceof UdfpsKeyguardViewController)) {
+            // always rotate view if we're not on the keyguard
+            return true;
+        }
+
+        // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
+        if (mKeyguardUpdateMonitor.isGoingToSleep() || !mKeyguardStateController.isOccluded()) {
+            return false;
+        }
+
+        return true;
+    }
+
     private WindowManager.LayoutParams computeLayoutParams(
             @Nullable UdfpsAnimationViewController animation) {
         final int paddingX = animation != null ? animation.getPaddingX() : 0;
@@ -685,9 +697,11 @@
         // Transform dimensions if the device is in landscape mode
         switch (mContext.getDisplay().getRotation()) {
             case Surface.ROTATION_90:
-                if (animation instanceof UdfpsKeyguardViewController
-                        && mKeyguardUpdateMonitor.isGoingToSleep()) {
+                if (!shouldRotate(animation)) {
+                    Log.v(TAG, "skip rotating udfps location ROTATION_90");
                     break;
+                } else {
+                    Log.v(TAG, "rotate udfps location ROTATION_90");
                 }
                 mCoreLayoutParams.x = location.sensorLocationY - location.sensorRadius
                         - paddingX;
@@ -696,9 +710,11 @@
                 break;
 
             case Surface.ROTATION_270:
-                if (animation instanceof UdfpsKeyguardViewController
-                        && mKeyguardUpdateMonitor.isGoingToSleep()) {
+                if (!shouldRotate(animation)) {
+                    Log.v(TAG, "skip rotating udfps location ROTATION_270");
                     break;
+                } else {
+                    Log.v(TAG, "rotate udfps location ROTATION_270");
                 }
                 mCoreLayoutParams.x = p.x - location.sensorLocationY - location.sensorRadius
                         - paddingX;
@@ -795,13 +811,12 @@
                         mStatusBarOptional,
                         mKeyguardViewManager,
                         mKeyguardUpdateMonitor,
-                        mFgExecutor,
                         mDumpManager,
-                        mKeyguardViewMediator,
                         mLockscreenShadeTransitionController,
                         mConfigurationController,
                         mSystemClock,
                         mKeyguardStateController,
+                        mUnlockedScreenOffAnimationController,
                         this
                 );
             case BiometricOverlayConstants.REASON_AUTH_BP:
@@ -866,6 +881,10 @@
             return;
         }
 
+        if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
+            return;
+        }
+
         mAodInterruptRunnable = () -> {
             mIsAodInterruptActive = true;
             // Since the sensor that triggers the AOD interrupt doesn't provide
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index d407756..2034ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -16,9 +16,11 @@
 
 package com.android.systemui.biometrics;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -26,11 +28,17 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.TypedValue;
 import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 
+import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.graphics.ColorUtils;
 import com.android.systemui.R;
 
 /**
@@ -39,10 +47,20 @@
 public class UdfpsEnrollDrawable extends UdfpsDrawable {
     private static final String TAG = "UdfpsAnimationEnroll";
 
-    private static final long ANIM_DURATION = 800;
+    private static final long HINT_COLOR_ANIM_DELAY_MS = 233L;
+    private static final long HINT_COLOR_ANIM_DURATION_MS = 517L;
+    private static final long HINT_WIDTH_ANIM_DURATION_MS = 233L;
+    private static final long TARGET_ANIM_DURATION_LONG = 800L;
+    private static final long TARGET_ANIM_DURATION_SHORT = 600L;
     // 1 + SCALE_MAX is the maximum that the moving target will animate to
     private static final float SCALE_MAX = 0.25f;
 
+    private static final float HINT_PADDING_DP = 10f;
+    private static final float HINT_MAX_WIDTH_DP = 6f;
+    private static final float HINT_ANGLE = 40f;
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
     @NonNull private final Drawable mMovingTargetFpIcon;
     @NonNull private final Paint mSensorOutlinePaint;
     @NonNull private final Paint mBlueFill;
@@ -51,17 +69,41 @@
     @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
     // Moving target animator set
-    @Nullable AnimatorSet mAnimatorSet;
+    @Nullable AnimatorSet mTargetAnimatorSet;
     // Moving target location
     float mCurrentX;
     float mCurrentY;
     // Moving target size
     float mCurrentScale = 1.f;
 
+    @ColorInt private final int mHintColorFaded;
+    @ColorInt private final int mHintColorHighlight;
+    private final float mHintMaxWidthPx;
+    private final float mHintPaddingPx;
+
+    @NonNull private final Animator.AnimatorListener mTargetAnimListener;
+
+    private boolean mShouldShowTipHint = false;
+    @NonNull private final Paint mTipHintPaint;
+    @Nullable private AnimatorSet mTipHintAnimatorSet;
+    @Nullable private ValueAnimator mTipHintColorAnimator;
+    @Nullable private ValueAnimator mTipHintWidthAnimator;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintColorUpdateListener;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintWidthUpdateListener;
+    @NonNull private final Animator.AnimatorListener mTipHintPulseListener;
+
+    private boolean mShouldShowEdgeHint = false;
+    @NonNull private final Paint mEdgeHintPaint;
+    @Nullable private AnimatorSet mEdgeHintAnimatorSet;
+    @Nullable private ValueAnimator mEdgeHintColorAnimator;
+    @Nullable private ValueAnimator mEdgeHintWidthAnimator;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintColorUpdateListener;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener;
+    @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener;
+
     UdfpsEnrollDrawable(@NonNull Context context) {
         super(context);
 
-
         mSensorOutlinePaint = new Paint(0 /* flags */);
         mSensorOutlinePaint.setAntiAlias(true);
         mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon));
@@ -78,6 +120,117 @@
         mMovingTargetFpIcon.mutate();
 
         mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
+
+        mHintColorFaded = getHintColorFaded(context);
+        mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
+        mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
+        mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
+
+        mTargetAnimListener = new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {}
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                updateTipHintVisibility();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {}
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {}
+        };
+
+        mTipHintPaint = new Paint(0 /* flags */);
+        mTipHintPaint.setAntiAlias(true);
+        mTipHintPaint.setColor(mHintColorFaded);
+        mTipHintPaint.setStyle(Paint.Style.STROKE);
+        mTipHintPaint.setStrokeCap(Paint.Cap.ROUND);
+        mTipHintPaint.setStrokeWidth(0f);
+        mTipHintColorUpdateListener = animation -> {
+            mTipHintPaint.setColor((int) animation.getAnimatedValue());
+            invalidateSelf();
+        };
+        mTipHintWidthUpdateListener = animation -> {
+            mTipHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
+            invalidateSelf();
+        };
+        mTipHintPulseListener = new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {}
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mHandler.postDelayed(() -> {
+                    mTipHintColorAnimator =
+                            ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorFaded);
+                    mTipHintColorAnimator.setInterpolator(new LinearInterpolator());
+                    mTipHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
+                    mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
+                    mTipHintColorAnimator.start();
+                }, HINT_COLOR_ANIM_DELAY_MS);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {}
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {}
+        };
+
+        mEdgeHintPaint = new Paint(0 /* flags */);
+        mEdgeHintPaint.setAntiAlias(true);
+        mEdgeHintPaint.setColor(mHintColorFaded);
+        mEdgeHintPaint.setStyle(Paint.Style.STROKE);
+        mEdgeHintPaint.setStrokeCap(Paint.Cap.ROUND);
+        mEdgeHintPaint.setStrokeWidth(0f);
+        mEdgeHintColorUpdateListener = animation -> {
+            mEdgeHintPaint.setColor((int) animation.getAnimatedValue());
+            invalidateSelf();
+        };
+        mEdgeHintWidthUpdateListener = animation -> {
+            mEdgeHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
+            invalidateSelf();
+        };
+        mEdgeHintPulseListener = new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {}
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mHandler.postDelayed(() -> {
+                    mEdgeHintColorAnimator =
+                            ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorFaded);
+                    mEdgeHintColorAnimator.setInterpolator(new LinearInterpolator());
+                    mEdgeHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
+                    mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
+                    mEdgeHintColorAnimator.start();
+                }, HINT_COLOR_ANIM_DELAY_MS);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {}
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {}
+        };
+    }
+
+    @ColorInt
+    private static int getHintColorFaded(@NonNull Context context) {
+        final TypedValue tv = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
+        final int alpha = (int) (tv.getFloat() * 255f);
+
+        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
+        final TypedArray ta = context.obtainStyledAttributes(attrs);
+        try {
+            @ColorInt final int color = ta.getColor(0, context.getColor(R.color.white_disabled));
+            return ColorUtils.setAlphaComponent(color, alpha);
+        } finally {
+            ta.recycle();
+        }
     }
 
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
@@ -98,41 +251,154 @@
     }
 
     void onEnrollmentProgress(int remaining, int totalSteps) {
-        if (mEnrollHelper.isCenterEnrollmentComplete()) {
-            if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
-                mAnimatorSet.end();
+        if (mEnrollHelper == null) {
+            return;
+        }
+
+        if (!mEnrollHelper.isCenterEnrollmentStage()) {
+            if (mTargetAnimatorSet != null && mTargetAnimatorSet.isRunning()) {
+                mTargetAnimatorSet.end();
             }
 
             final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
+            if (mCurrentX != point.x || mCurrentY != point.y) {
+                final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x);
+                x.addUpdateListener(animation -> {
+                    mCurrentX = (float) animation.getAnimatedValue();
+                    invalidateSelf();
+                });
 
-            final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x);
-            x.addUpdateListener(animation -> {
-                mCurrentX = (float) animation.getAnimatedValue();
-                invalidateSelf();
-            });
+                final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y);
+                y.addUpdateListener(animation -> {
+                    mCurrentY = (float) animation.getAnimatedValue();
+                    invalidateSelf();
+                });
 
-            final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y);
-            y.addUpdateListener(animation -> {
-                mCurrentY = (float) animation.getAnimatedValue();
-                invalidateSelf();
-            });
+                final boolean isMovingToCenter = point.x == 0f && point.y == 0f;
+                final long duration = isMovingToCenter
+                        ? TARGET_ANIM_DURATION_SHORT
+                        : TARGET_ANIM_DURATION_LONG;
 
-            final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI);
-            scale.setDuration(ANIM_DURATION);
-            scale.addUpdateListener(animation -> {
-                // Grow then shrink
-                mCurrentScale = 1 +
-                        SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
-                invalidateSelf();
-            });
+                final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI);
+                scale.setDuration(duration);
+                scale.addUpdateListener(animation -> {
+                    // Grow then shrink
+                    mCurrentScale = 1
+                            + SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue());
+                    invalidateSelf();
+                });
 
-            mAnimatorSet = new AnimatorSet();
+                mTargetAnimatorSet = new AnimatorSet();
 
-            mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
-            mAnimatorSet.setDuration(ANIM_DURATION);
-            mAnimatorSet.playTogether(x, y, scale);
-            mAnimatorSet.start();
+                mTargetAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
+                mTargetAnimatorSet.setDuration(duration);
+                mTargetAnimatorSet.addListener(mTargetAnimListener);
+                mTargetAnimatorSet.playTogether(x, y, scale);
+                mTargetAnimatorSet.start();
+            } else {
+                updateTipHintVisibility();
+            }
+        } else {
+            updateTipHintVisibility();
         }
+
+        updateEdgeHintVisibility();
+    }
+
+    private void updateTipHintVisibility() {
+        final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage();
+        if (mShouldShowTipHint == shouldShow) {
+            return;
+        }
+        mShouldShowTipHint = shouldShow;
+
+        if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) {
+            mTipHintWidthAnimator.cancel();
+        }
+
+        final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
+        mTipHintWidthAnimator = ValueAnimator.ofFloat(mTipHintPaint.getStrokeWidth(), targetWidth);
+        mTipHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
+        mTipHintWidthAnimator.addUpdateListener(mTipHintWidthUpdateListener);
+
+        if (shouldShow) {
+            startTipHintPulseAnimation();
+        } else {
+            mTipHintWidthAnimator.start();
+        }
+    }
+
+    private void updateEdgeHintVisibility() {
+        final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isEdgeEnrollmentStage();
+        if (mShouldShowEdgeHint == shouldShow) {
+            return;
+        }
+        mShouldShowEdgeHint = shouldShow;
+
+        if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) {
+            mEdgeHintWidthAnimator.cancel();
+        }
+
+        final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
+        mEdgeHintWidthAnimator =
+                ValueAnimator.ofFloat(mEdgeHintPaint.getStrokeWidth(), targetWidth);
+        mEdgeHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
+        mEdgeHintWidthAnimator.addUpdateListener(mEdgeHintWidthUpdateListener);
+
+        if (shouldShow) {
+            startEdgeHintPulseAnimation();
+        } else {
+            mEdgeHintWidthAnimator.start();
+        }
+    }
+
+    private void startTipHintPulseAnimation() {
+        mHandler.removeCallbacksAndMessages(null);
+        if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) {
+            mTipHintAnimatorSet.cancel();
+        }
+        if (mTipHintColorAnimator != null && mTipHintColorAnimator.isRunning()) {
+            mTipHintColorAnimator.cancel();
+        }
+
+        mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorHighlight);
+        mTipHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
+        mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
+        mTipHintColorAnimator.addListener(mTipHintPulseListener);
+
+        mTipHintAnimatorSet = new AnimatorSet();
+        mTipHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
+        mTipHintAnimatorSet.playTogether(mTipHintColorAnimator, mTipHintWidthAnimator);
+        mTipHintAnimatorSet.start();
+    }
+
+    private void startEdgeHintPulseAnimation() {
+        mHandler.removeCallbacksAndMessages(null);
+        if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) {
+            mEdgeHintAnimatorSet.cancel();
+        }
+        if (mEdgeHintColorAnimator != null && mEdgeHintColorAnimator.isRunning()) {
+            mEdgeHintColorAnimator.cancel();
+        }
+
+        mEdgeHintColorAnimator =
+                ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorHighlight);
+        mEdgeHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
+        mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
+        mEdgeHintColorAnimator.addListener(mEdgeHintPulseListener);
+
+        mEdgeHintAnimatorSet = new AnimatorSet();
+        mEdgeHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
+        mEdgeHintAnimatorSet.playTogether(mEdgeHintColorAnimator, mEdgeHintWidthAnimator);
+        mEdgeHintAnimatorSet.start();
+    }
+
+    private boolean isTipHintVisible() {
+        return mTipHintPaint.getStrokeWidth() > 0f;
+    }
+
+    private boolean isEdgeHintVisible() {
+        return mEdgeHintPaint.getStrokeWidth() > 0f;
     }
 
     @Override
@@ -142,7 +408,7 @@
         }
 
         // Draw moving target
-        if (mEnrollHelper.isCenterEnrollmentComplete()) {
+        if (mEnrollHelper != null && !mEnrollHelper.isCenterEnrollmentStage()) {
             canvas.save();
             canvas.translate(mCurrentX, mCurrentY);
 
@@ -162,6 +428,59 @@
             mFingerprintDrawable.setAlpha(mAlpha);
             mSensorOutlinePaint.setAlpha(mAlpha);
         }
+
+        // Draw the finger tip or edges hint.
+        if (isTipHintVisible() || isEdgeHintVisible()) {
+            canvas.save();
+
+            // Make arcs start from the top, rather than the right.
+            canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
+
+            final float halfSensorHeight = Math.abs(mSensorRect.bottom - mSensorRect.top) / 2f;
+            final float halfSensorWidth = Math.abs(mSensorRect.right - mSensorRect.left) / 2f;
+            final float hintXOffset = halfSensorWidth + mHintPaddingPx;
+            final float hintYOffset = halfSensorHeight + mHintPaddingPx;
+
+            if (isTipHintVisible()) {
+                canvas.drawArc(
+                        mSensorRect.centerX() - hintXOffset,
+                        mSensorRect.centerY() - hintYOffset,
+                        mSensorRect.centerX() + hintXOffset,
+                        mSensorRect.centerY() + hintYOffset,
+                        -HINT_ANGLE / 2f,
+                        HINT_ANGLE,
+                        false /* useCenter */,
+                        mTipHintPaint);
+            }
+
+            if (isEdgeHintVisible()) {
+                // Draw right edge hint.
+                canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
+                canvas.drawArc(
+                        mSensorRect.centerX() - hintXOffset,
+                        mSensorRect.centerY() - hintYOffset,
+                        mSensorRect.centerX() + hintXOffset,
+                        mSensorRect.centerY() + hintYOffset,
+                        -HINT_ANGLE / 2f,
+                        HINT_ANGLE,
+                        false /* useCenter */,
+                        mEdgeHintPaint);
+
+                // Draw left edge hint.
+                canvas.rotate(180f, mSensorRect.centerX(), mSensorRect.centerY());
+                canvas.drawArc(
+                        mSensorRect.centerX() - hintXOffset,
+                        mSensorRect.centerY() - hintYOffset,
+                        mSensorRect.centerX() + hintXOffset,
+                        mSensorRect.centerY() + hintYOffset,
+                        -HINT_ANGLE / 2f,
+                        HINT_ANGLE,
+                        false /* useCenter */,
+                        mEdgeHintPaint);
+            }
+
+            canvas.restore();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index c6d2192..d5c763d3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -44,16 +45,14 @@
     private static final String NEW_COORDS_OVERRIDE =
             "com.android.systemui.biometrics.UdfpsNewCoords";
 
-    // Enroll with two center touches before going to guided enrollment
-    private static final int NUM_CENTER_TOUCHES = 2;
-
     interface Listener {
         void onEnrollmentProgress(int remaining, int totalSteps);
+        void onEnrollmentHelp(int remaining, int totalSteps);
         void onLastStepAcquired();
-        void onEnrollmentHelp();
     }
 
     @NonNull private final Context mContext;
+    @NonNull private final FingerprintManager mFingerprintManager;
     // IUdfpsOverlayController reason
     private final int mEnrollReason;
     private final boolean mAccessibilityEnabled;
@@ -66,10 +65,15 @@
     // interface makes no promises about monotonically increasing by one each time.
     private int mLocationsEnrolled = 0;
 
+    private int mCenterTouchCount = 0;
+
     @Nullable Listener mListener;
 
-    public UdfpsEnrollHelper(@NonNull Context context, int reason) {
+    public UdfpsEnrollHelper(@NonNull Context context,
+            @NonNull FingerprintManager fingerprintManager, int reason) {
+
         mContext = context;
+        mFingerprintManager = fingerprintManager;
         mEnrollReason = reason;
 
         final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
@@ -118,6 +122,14 @@
         }
     }
 
+    int getStageCount() {
+        return mFingerprintManager.getEnrollStageCount();
+    }
+
+    int getStageThresholdSteps(int totalSteps, int stageIndex) {
+        return Math.round(totalSteps * mFingerprintManager.getEnrollStageThreshold(stageIndex));
+    }
+
     boolean shouldShowProgressBar() {
         return mEnrollReason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
     }
@@ -129,6 +141,9 @@
 
         if (remaining != mRemainingSteps) {
             mLocationsEnrolled++;
+            if (isCenterEnrollmentStage()) {
+                mCenterTouchCount++;
+            }
         }
 
         mRemainingSteps = remaining;
@@ -140,7 +155,7 @@
 
     void onEnrollmentHelp() {
         if (mListener != null) {
-            mListener.onEnrollmentHelp();
+            mListener.onEnrollmentHelp(mRemainingSteps, mTotalSteps);
         }
     }
 
@@ -155,19 +170,41 @@
         }
     }
 
-    boolean isCenterEnrollmentComplete() {
+    boolean isCenterEnrollmentStage() {
         if (mTotalSteps == -1 || mRemainingSteps == -1) {
-            return false;
-        } else if (mAccessibilityEnabled) {
+            return true;
+        }
+        return mTotalSteps - mRemainingSteps < getStageThresholdSteps(mTotalSteps, 0);
+    }
+
+    boolean isGuidedEnrollmentStage() {
+        if (mAccessibilityEnabled || mTotalSteps == -1 || mRemainingSteps == -1) {
             return false;
         }
-        final int stepsEnrolled = mTotalSteps - mRemainingSteps;
-        return stepsEnrolled >= NUM_CENTER_TOUCHES;
+        final int progressSteps = mTotalSteps - mRemainingSteps;
+        return progressSteps >= getStageThresholdSteps(mTotalSteps, 0)
+                && progressSteps < getStageThresholdSteps(mTotalSteps, 1);
+    }
+
+    boolean isTipEnrollmentStage() {
+        if (mTotalSteps == -1 || mRemainingSteps == -1) {
+            return false;
+        }
+        final int progressSteps = mTotalSteps - mRemainingSteps;
+        return progressSteps >= getStageThresholdSteps(mTotalSteps, 1)
+                && progressSteps < getStageThresholdSteps(mTotalSteps, 2);
+    }
+
+    boolean isEdgeEnrollmentStage() {
+        if (mTotalSteps == -1 || mRemainingSteps == -1) {
+            return false;
+        }
+        return mTotalSteps - mRemainingSteps >= getStageThresholdSteps(mTotalSteps, 2);
     }
 
     @NonNull
     PointF getNextGuidedEnrollmentPoint() {
-        if (mAccessibilityEnabled) {
+        if (mAccessibilityEnabled || !isGuidedEnrollmentStage()) {
             return new PointF(0f, 0f);
         }
 
@@ -177,7 +214,7 @@
                     SCALE_OVERRIDE, SCALE,
                     UserHandle.USER_CURRENT);
         }
-        final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
+        final int index = mLocationsEnrolled - mCenterTouchCount;
         final PointF originalPoint = mGuidedEnrollmentPoints
                 .get(index % mGuidedEnrollmentPoints.size());
         return new PointF(originalPoint.x * scale, originalPoint.y * scale);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 373d17c8..b2a5409 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -16,163 +16,129 @@
 
 package com.android.systemui.biometrics;
 
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.annotation.ColorInt;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
-import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
-import android.util.TypedValue;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.systemui.R;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * UDFPS enrollment progress bar.
  */
 public class UdfpsEnrollProgressBarDrawable extends Drawable {
+    private static final String TAG = "UdfpsProgressBar";
 
-    private static final String TAG = "UdfpsEnrollProgressBarDrawable";
-
-    private static final float PROGRESS_BAR_THICKNESS_DP = 12;
+    private static final float SEGMENT_GAP_ANGLE = 12f;
 
     @NonNull private final Context mContext;
-    @NonNull private final Paint mBackgroundCirclePaint;
-    @NonNull private final Paint mProgressPaint;
 
-    @Nullable private ValueAnimator mProgressAnimator;
-    @Nullable private ValueAnimator mProgressShowingHelpAnimator;
-    @Nullable private ValueAnimator mProgressHidingHelpAnimator;
-    @ColorInt private final int mProgressColor;
-    @ColorInt private final int mProgressHelpColor;
-    private final int mShortAnimationDuration;
-    private float mProgress;
-    private int mRotation; // After last step, rotate the progress bar once
-    private boolean mLastStepAcquired;
+    @Nullable private UdfpsEnrollHelper mEnrollHelper;
+    @NonNull private List<UdfpsEnrollProgressBarSegment> mSegments = new ArrayList<>();
+    private int mTotalSteps = 1;
+    private int mProgressSteps = 0;
+    private boolean mIsShowingHelp = false;
 
     public UdfpsEnrollProgressBarDrawable(@NonNull Context context) {
         mContext = context;
-
-        mShortAnimationDuration = context.getResources()
-                .getInteger(com.android.internal.R.integer.config_shortAnimTime);
-        mProgressColor = context.getColor(R.color.udfps_enroll_progress);
-        mProgressHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
-
-        mBackgroundCirclePaint = new Paint();
-        mBackgroundCirclePaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
-        mBackgroundCirclePaint.setColor(context.getColor(R.color.white_disabled));
-        mBackgroundCirclePaint.setAntiAlias(true);
-        mBackgroundCirclePaint.setStyle(Paint.Style.STROKE);
-
-        // Background circle color + alpha
-        TypedArray tc = context.obtainStyledAttributes(
-                new int[] {android.R.attr.colorControlNormal});
-        int tintColor = tc.getColor(0, mBackgroundCirclePaint.getColor());
-        mBackgroundCirclePaint.setColor(tintColor);
-        tc.recycle();
-        TypedValue alpha = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
-        mBackgroundCirclePaint.setAlpha((int) (alpha.getFloat() * 255));
-
-        // Progress should not be color extracted
-        mProgressPaint = new Paint();
-        mProgressPaint.setStrokeWidth(Utils.dpToPixels(context, PROGRESS_BAR_THICKNESS_DP));
-        mProgressPaint.setColor(mProgressColor);
-        mProgressPaint.setAntiAlias(true);
-        mProgressPaint.setStyle(Paint.Style.STROKE);
-        mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
     }
 
-    void setEnrollmentProgress(int remaining, int totalSteps) {
-        // Add one so that the first steps actually changes progress, but also so that the last
-        // step ends at 1.0
-        final float progress = (totalSteps - remaining + 1) / (float) (totalSteps + 1);
-        setEnrollmentProgress(progress);
-    }
-
-    private void setEnrollmentProgress(float progress) {
-        if (mLastStepAcquired) {
-            return;
-        }
-
-        long animationDuration = mShortAnimationDuration;
-
-        hideEnrollmentHelp();
-
-        if (progress == 1.f) {
-            animationDuration = 400;
-            final ValueAnimator rotationAnimator = ValueAnimator.ofInt(0, 400);
-            rotationAnimator.setDuration(animationDuration);
-            rotationAnimator.addUpdateListener(animation -> {
-                Log.d(TAG, "Rotation: " + mRotation);
-                mRotation = (int) animation.getAnimatedValue();
-                invalidateSelf();
-            });
-            rotationAnimator.start();
-        }
-
-        if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
-            mProgressAnimator.cancel();
-        }
-
-        mProgressAnimator = ValueAnimator.ofFloat(mProgress, progress);
-        mProgressAnimator.setDuration(animationDuration);
-        mProgressAnimator.addUpdateListener(animation -> {
-            mProgress = (float) animation.getAnimatedValue();
+    void setEnrollHelper(@Nullable UdfpsEnrollHelper enrollHelper) {
+        mEnrollHelper = enrollHelper;
+        if (enrollHelper != null) {
+            final int stageCount = enrollHelper.getStageCount();
+            mSegments = new ArrayList<>(stageCount);
+            float startAngle = SEGMENT_GAP_ANGLE / 2f;
+            final float sweepAngle = (360f / stageCount) - SEGMENT_GAP_ANGLE;
+            final Runnable invalidateRunnable = this::invalidateSelf;
+            for (int index = 0; index < stageCount; index++) {
+                mSegments.add(new UdfpsEnrollProgressBarSegment(mContext, getBounds(), startAngle,
+                        sweepAngle, SEGMENT_GAP_ANGLE, invalidateRunnable));
+                startAngle += sweepAngle + SEGMENT_GAP_ANGLE;
+            }
             invalidateSelf();
-        });
-        mProgressAnimator.start();
+        }
+    }
+
+    void onEnrollmentProgress(int remaining, int totalSteps) {
+        mTotalSteps = totalSteps;
+        updateState(getProgressSteps(remaining, totalSteps), false /* isShowingHelp */);
+    }
+
+    void onEnrollmentHelp(int remaining, int totalSteps) {
+        updateState(getProgressSteps(remaining, totalSteps), true /* isShowingHelp */);
     }
 
     void onLastStepAcquired() {
-        setEnrollmentProgress(1.f);
-        mLastStepAcquired = true;
+        updateState(mTotalSteps, false /* isShowingHelp */);
     }
 
-    void onEnrollmentHelp() {
-        if (mProgressShowingHelpAnimator != null || mProgressAnimator == null) {
-            return; // already showing or at 0% (no progress bar visible)
-        }
-
-        if (mProgressHidingHelpAnimator != null && mProgressHidingHelpAnimator.isRunning()) {
-            mProgressHidingHelpAnimator.cancel();
-        }
-        mProgressHidingHelpAnimator = null;
-
-        mProgressShowingHelpAnimator = getProgressColorAnimator(
-                mProgressPaint.getColor(), mProgressHelpColor);
-        mProgressShowingHelpAnimator.start();
+    private static int getProgressSteps(int remaining, int totalSteps) {
+        // Show some progress for the initial touch.
+        return Math.max(1, totalSteps - remaining);
     }
 
-    private void hideEnrollmentHelp() {
-        if (mProgressHidingHelpAnimator != null || mProgressShowingHelpAnimator == null) {
-            return; // already hidden or help never shown
-        }
-
-        if (mProgressShowingHelpAnimator != null && mProgressShowingHelpAnimator.isRunning()) {
-            mProgressShowingHelpAnimator.cancel();
-        }
-        mProgressShowingHelpAnimator = null;
-
-        mProgressHidingHelpAnimator = getProgressColorAnimator(
-                mProgressPaint.getColor(), mProgressColor);
-        mProgressHidingHelpAnimator.start();
+    private void updateState(int progressSteps, boolean isShowingHelp) {
+        updateProgress(progressSteps);
+        updateFillColor(isShowingHelp);
     }
 
-    private ValueAnimator getProgressColorAnimator(@ColorInt int from, @ColorInt int to) {
-        final ValueAnimator animator = ValueAnimator.ofObject(
-                ArgbEvaluator.getInstance(), from, to);
-        animator.setDuration(mShortAnimationDuration);
-        animator.addUpdateListener(animation -> {
-            mProgressPaint.setColor((int) animation.getAnimatedValue());
-        });
-        return animator;
+    private void updateProgress(int progressSteps) {
+        if (mProgressSteps == progressSteps) {
+            return;
+        }
+        mProgressSteps = progressSteps;
+
+        if (mEnrollHelper == null) {
+            Log.e(TAG, "updateState: UDFPS enroll helper was null");
+            return;
+        }
+
+        int index = 0;
+        int prevThreshold = 0;
+        while (index < mSegments.size()) {
+            final UdfpsEnrollProgressBarSegment segment = mSegments.get(index);
+            final int thresholdSteps = mEnrollHelper.getStageThresholdSteps(mTotalSteps, index);
+            if (progressSteps >= thresholdSteps && segment.getProgress() < 1f) {
+                segment.updateProgress(1f);
+                break;
+            } else if (progressSteps >= prevThreshold && progressSteps < thresholdSteps) {
+                final int relativeSteps = progressSteps - prevThreshold;
+                final int relativeThreshold = thresholdSteps - prevThreshold;
+                final float segmentProgress = (float) relativeSteps / (float) relativeThreshold;
+                segment.updateProgress(segmentProgress);
+                break;
+            }
+
+            index++;
+            prevThreshold = thresholdSteps;
+        }
+
+        if (progressSteps >= mTotalSteps) {
+            for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
+                segment.startCompletionAnimation();
+            }
+        } else {
+            for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
+                segment.cancelCompletionAnimation();
+            }
+        }
+    }
+
+    private void updateFillColor(boolean isShowingHelp) {
+        if (mIsShowingHelp == isShowingHelp) {
+            return;
+        }
+        mIsShowingHelp = isShowingHelp;
+
+        for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
+            segment.updateFillColor(isShowingHelp);
+        }
     }
 
     @Override
@@ -180,43 +146,22 @@
         canvas.save();
 
         // Progress starts from the top, instead of the right
-        canvas.rotate(-90 + mRotation, getBounds().centerX(), getBounds().centerY());
+        canvas.rotate(-90f, getBounds().centerX(), getBounds().centerY());
 
-        // Progress bar "background track"
-        final float halfPaddingPx = Utils.dpToPixels(mContext, PROGRESS_BAR_THICKNESS_DP) / 2;
-        canvas.drawArc(halfPaddingPx,
-                halfPaddingPx,
-                getBounds().right - halfPaddingPx,
-                getBounds().bottom - halfPaddingPx,
-                0,
-                360,
-                false,
-                mBackgroundCirclePaint
-        );
-
-        final float progress = 360.f * mProgress;
-        // Progress
-        canvas.drawArc(halfPaddingPx,
-                halfPaddingPx,
-                getBounds().right - halfPaddingPx,
-                getBounds().bottom - halfPaddingPx,
-                0,
-                progress,
-                false,
-                mProgressPaint
-        );
+        // Draw each of the enroll segments.
+        for (final UdfpsEnrollProgressBarSegment segment : mSegments) {
+            segment.draw(canvas);
+        }
 
         canvas.restore();
     }
 
     @Override
     public void setAlpha(int alpha) {
-
     }
 
     @Override
     public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java
new file mode 100644
index 0000000..bd6ab44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarSegment.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * A single segment of the UDFPS enrollment progress bar.
+ */
+public class UdfpsEnrollProgressBarSegment {
+    private static final String TAG = "UdfpsProgressBarSegment";
+
+    private static final long FILL_COLOR_ANIMATION_DURATION_MS = 200L;
+    private static final long PROGRESS_ANIMATION_DURATION_MS = 400L;
+    private static final long OVER_SWEEP_ANIMATION_DELAY_MS = 200L;
+    private static final long OVER_SWEEP_ANIMATION_DURATION_MS = 200L;
+
+    private static final float STROKE_WIDTH_DP = 12f;
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    @NonNull private final Rect mBounds;
+    @NonNull private final Runnable mInvalidateRunnable;
+    private final float mStartAngle;
+    private final float mSweepAngle;
+    private final float mMaxOverSweepAngle;
+    private final float mStrokeWidthPx;
+    @ColorInt private final int mProgressColor;
+    @ColorInt private final int mHelpColor;
+
+    @NonNull private final Paint mBackgroundPaint;
+    @NonNull private final Paint mProgressPaint;
+
+    private float mProgress = 0f;
+    private float mAnimatedProgress = 0f;
+    @Nullable private ValueAnimator mProgressAnimator;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mProgressUpdateListener;
+
+    private boolean mIsShowingHelp = false;
+    @Nullable private ValueAnimator mFillColorAnimator;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mFillColorUpdateListener;
+
+    private float mOverSweepAngle = 0f;
+    @Nullable private ValueAnimator mOverSweepAnimator;
+    @Nullable private ValueAnimator mOverSweepReverseAnimator;
+    @NonNull private final ValueAnimator.AnimatorUpdateListener mOverSweepUpdateListener;
+    @NonNull private final Runnable mOverSweepAnimationRunnable;
+
+    public UdfpsEnrollProgressBarSegment(@NonNull Context context, @NonNull Rect bounds,
+            float startAngle, float sweepAngle, float maxOverSweepAngle,
+            @NonNull Runnable invalidateRunnable) {
+
+        mBounds = bounds;
+        mInvalidateRunnable = invalidateRunnable;
+        mStartAngle = startAngle;
+        mSweepAngle = sweepAngle;
+        mMaxOverSweepAngle = maxOverSweepAngle;
+        mStrokeWidthPx = Utils.dpToPixels(context, STROKE_WIDTH_DP);
+        mProgressColor = context.getColor(R.color.udfps_enroll_progress);
+        mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
+
+        mBackgroundPaint = new Paint();
+        mBackgroundPaint.setStrokeWidth(mStrokeWidthPx);
+        mBackgroundPaint.setColor(context.getColor(R.color.white_disabled));
+        mBackgroundPaint.setAntiAlias(true);
+        mBackgroundPaint.setStyle(Paint.Style.STROKE);
+        mBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
+
+        // Background paint color + alpha
+        final int[] attrs = new int[] {android.R.attr.colorControlNormal};
+        final TypedArray ta = context.obtainStyledAttributes(attrs);
+        @ColorInt final int tintColor = ta.getColor(0, mBackgroundPaint.getColor());
+        mBackgroundPaint.setColor(tintColor);
+        ta.recycle();
+        TypedValue alpha = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, alpha, true);
+        mBackgroundPaint.setAlpha((int) (alpha.getFloat() * 255f));
+
+        // Progress should not be color extracted
+        mProgressPaint = new Paint();
+        mProgressPaint.setStrokeWidth(mStrokeWidthPx);
+        mProgressPaint.setColor(mProgressColor);
+        mProgressPaint.setAntiAlias(true);
+        mProgressPaint.setStyle(Paint.Style.STROKE);
+        mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+
+        mProgressUpdateListener = animation -> {
+            mAnimatedProgress = (float) animation.getAnimatedValue();
+            mInvalidateRunnable.run();
+        };
+
+        mFillColorUpdateListener = animation -> {
+            mProgressPaint.setColor((int) animation.getAnimatedValue());
+            mInvalidateRunnable.run();
+        };
+
+        mOverSweepUpdateListener = animation -> {
+            mOverSweepAngle = (float) animation.getAnimatedValue();
+            mInvalidateRunnable.run();
+        };
+        mOverSweepAnimationRunnable = () -> {
+            if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
+                mOverSweepAnimator.cancel();
+            }
+            mOverSweepAnimator = ValueAnimator.ofFloat(mOverSweepAngle, mMaxOverSweepAngle);
+            mOverSweepAnimator.setDuration(OVER_SWEEP_ANIMATION_DURATION_MS);
+            mOverSweepAnimator.addUpdateListener(mOverSweepUpdateListener);
+            mOverSweepAnimator.start();
+        };
+    }
+
+    /**
+     * Draws this segment to the given canvas.
+     */
+    public void draw(@NonNull Canvas canvas) {
+        final float halfPaddingPx = mStrokeWidthPx / 2f;
+
+        if (mAnimatedProgress < 1f) {
+            // Draw the unfilled background color of the segment.
+            canvas.drawArc(
+                    halfPaddingPx,
+                    halfPaddingPx,
+                    mBounds.right - halfPaddingPx,
+                    mBounds.bottom - halfPaddingPx,
+                    mStartAngle,
+                    mSweepAngle,
+                    false /* useCenter */,
+                    mBackgroundPaint);
+        }
+
+        if (mAnimatedProgress > 0f) {
+            // Draw the filled progress portion of the segment.
+            canvas.drawArc(
+                    halfPaddingPx,
+                    halfPaddingPx,
+                    mBounds.right - halfPaddingPx,
+                    mBounds.bottom - halfPaddingPx,
+                    mStartAngle,
+                    mSweepAngle * mAnimatedProgress + mOverSweepAngle,
+                    false /* useCenter */,
+                    mProgressPaint);
+        }
+    }
+
+    /**
+     * @return The fill progress of this segment, in the range [0, 1]. If fill progress is being
+     * animated, returns the value it is animating to.
+     */
+    public float getProgress() {
+        return mProgress;
+    }
+
+    /**
+     * Updates the fill progress of this segment, animating if necessary.
+     *
+     * @param progress The new fill progress, in the range [0, 1].
+     */
+    public void updateProgress(float progress) {
+        updateProgress(progress, PROGRESS_ANIMATION_DURATION_MS);
+    }
+
+    private void updateProgress(float progress, long animationDurationMs) {
+        if (mProgress == progress) {
+            return;
+        }
+        mProgress = progress;
+
+        if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
+            mProgressAnimator.cancel();
+        }
+
+        mProgressAnimator = ValueAnimator.ofFloat(mAnimatedProgress, progress);
+        mProgressAnimator.setDuration(animationDurationMs);
+        mProgressAnimator.addUpdateListener(mProgressUpdateListener);
+        mProgressAnimator.start();
+    }
+
+    /**
+     * Updates the fill color of this segment, animating if necessary.
+     *
+     * @param isShowingHelp Whether fill color should indicate that a help message is being shown.
+     */
+    public void updateFillColor(boolean isShowingHelp) {
+        if (mIsShowingHelp == isShowingHelp) {
+            return;
+        }
+        mIsShowingHelp = isShowingHelp;
+
+        if (mFillColorAnimator != null && mFillColorAnimator.isRunning()) {
+            mFillColorAnimator.cancel();
+        }
+
+        @ColorInt final int targetColor = isShowingHelp ? mHelpColor : mProgressColor;
+        mFillColorAnimator = ValueAnimator.ofArgb(mProgressPaint.getColor(), targetColor);
+        mFillColorAnimator.setDuration(FILL_COLOR_ANIMATION_DURATION_MS);
+        mFillColorAnimator.addUpdateListener(mFillColorUpdateListener);
+        mFillColorAnimator.start();
+    }
+
+    /**
+     * Queues and runs the completion animation for this segment.
+     */
+    public void startCompletionAnimation() {
+        final boolean hasCallback = mHandler.hasCallbacks(mOverSweepAnimationRunnable);
+        if (hasCallback || mOverSweepAngle >= mMaxOverSweepAngle) {
+            Log.d(TAG, "startCompletionAnimation skipped: hasCallback = " + hasCallback
+                    + ", mOverSweepAngle = " + mOverSweepAngle);
+            return;
+        }
+
+        // Reset sweep angle back to zero if the animation is being rolled back.
+        if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
+            mOverSweepReverseAnimator.cancel();
+            mOverSweepAngle = 0f;
+        }
+
+        // Clear help color and start filling the segment if it isn't already.
+        if (mAnimatedProgress < 1f) {
+            updateProgress(1f, OVER_SWEEP_ANIMATION_DELAY_MS);
+            updateFillColor(false /* isShowingHelp */);
+        }
+
+        // Queue the animation to run after fill completes.
+        mHandler.postDelayed(mOverSweepAnimationRunnable, OVER_SWEEP_ANIMATION_DELAY_MS);
+    }
+
+    /**
+     * Cancels (and reverses, if necessary) a queued or running completion animation.
+     */
+    public void cancelCompletionAnimation() {
+        // Cancel the animation if it's queued or running.
+        mHandler.removeCallbacks(mOverSweepAnimationRunnable);
+        if (mOverSweepAnimator != null && mOverSweepAnimator.isRunning()) {
+            mOverSweepAnimator.cancel();
+        }
+
+        // Roll back the animation if it has at least partially run.
+        if (mOverSweepAngle > 0f) {
+            if (mOverSweepReverseAnimator != null && mOverSweepReverseAnimator.isRunning()) {
+                mOverSweepReverseAnimator.cancel();
+            }
+
+            final float completion = mOverSweepAngle / mMaxOverSweepAngle;
+            final long proratedDuration = (long) (OVER_SWEEP_ANIMATION_DURATION_MS * completion);
+            mOverSweepReverseAnimator = ValueAnimator.ofFloat(mOverSweepAngle, 0f);
+            mOverSweepReverseAnimator.setDuration(proratedDuration);
+            mOverSweepReverseAnimator.addUpdateListener(mOverSweepUpdateListener);
+            mOverSweepReverseAnimator.start();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index c83006d..729838e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -73,23 +73,22 @@
     }
 
     void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
+        mFingerprintProgressDrawable.setEnrollHelper(enrollHelper);
         mFingerprintDrawable.setEnrollHelper(enrollHelper);
     }
 
     void onEnrollmentProgress(int remaining, int totalSteps) {
         mHandler.post(() -> {
-            mFingerprintProgressDrawable.setEnrollmentProgress(remaining, totalSteps);
+            mFingerprintProgressDrawable.onEnrollmentProgress(remaining, totalSteps);
             mFingerprintDrawable.onEnrollmentProgress(remaining, totalSteps);
         });
     }
 
-    void onLastStepAcquired() {
-        mHandler.post(() -> {
-            mFingerprintProgressDrawable.onLastStepAcquired();
-        });
+    void onEnrollmentHelp(int remaining, int totalSteps) {
+        mHandler.post(() -> mFingerprintProgressDrawable.onEnrollmentHelp(remaining, totalSteps));
     }
 
-    void onEnrollmentHelp() {
-        mHandler.post(mFingerprintProgressDrawable::onEnrollmentHelp);
+    void onLastStepAcquired() {
+        mHandler.post(mFingerprintProgressDrawable::onLastStepAcquired);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 33fbe7b..af7c352 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -35,21 +35,21 @@
     @NonNull private final UdfpsEnrollHelper mEnrollHelper;
     @NonNull private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
             new UdfpsEnrollHelper.Listener() {
-        @Override
-        public void onEnrollmentProgress(int remaining, int totalSteps) {
-            mView.onEnrollmentProgress(remaining, totalSteps);
-        }
+                @Override
+                public void onEnrollmentProgress(int remaining, int totalSteps) {
+                    mView.onEnrollmentProgress(remaining, totalSteps);
+                }
 
-        @Override
-        public void onLastStepAcquired() {
-            mView.onLastStepAcquired();
-        }
+                @Override
+                public void onEnrollmentHelp(int remaining, int totalSteps) {
+                    mView.onEnrollmentHelp(remaining, totalSteps);
+                }
 
-        @Override
-        public void onEnrollmentHelp() {
-            mView.onEnrollmentHelp();
-        }
-    };
+                @Override
+                public void onLastStepAcquired() {
+                    mView.onLastStepAcquired();
+                }
+            };
 
     protected UdfpsEnrollViewController(
             @NonNull UdfpsEnrollView view,
@@ -81,7 +81,7 @@
     @NonNull
     @Override
     public PointF getTouchTranslation() {
-        if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+        if (!mEnrollHelper.isGuidedEnrollmentStage()) {
             return new PointF(0, 0);
         } else {
             return mEnrollHelper.getNextGuidedEnrollmentPoint();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8ce4c3b..db93b26 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -26,36 +26,34 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Optional;
 
-
 /**
  * Class that coordinates non-HBM animations during keyguard authentication.
  */
 public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @NonNull private final DelayableExecutor mExecutor;
-    @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
     @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
     @NonNull private final ConfigurationController mConfigurationController;
     @NonNull private final SystemClock mSystemClock;
     @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final UdfpsController mUdfpsController;
+    @NonNull private final UnlockedScreenOffAnimationController
+            mUnlockedScreenOffAnimationController;
 
     private boolean mShowingUdfpsBouncer;
     private boolean mUdfpsRequested;
@@ -82,24 +80,22 @@
             @NonNull Optional<StatusBar> statusBarOptional,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull DelayableExecutor mainDelayableExecutor,
             @NonNull DumpManager dumpManager,
-            @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull LockscreenShadeTransitionController transitionController,
             @NonNull ConfigurationController configurationController,
             @NonNull SystemClock systemClock,
             @NonNull KeyguardStateController keyguardStateController,
+            @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull UdfpsController udfpsController) {
         super(view, statusBarStateController, statusBarOptional, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mExecutor = mainDelayableExecutor;
-        mKeyguardViewMediator = keyguardViewMediator;
         mLockScreenShadeTransitionController = transitionController;
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mKeyguardStateController = keyguardStateController;
         mUdfpsController = udfpsController;
+        mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
     }
 
     @Override
@@ -138,6 +134,7 @@
 
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
+        mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
     }
 
     @Override
@@ -156,6 +153,7 @@
         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
+        mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
     }
 
     @Override
@@ -428,4 +426,7 @@
                     updatePauseAuth();
                 }
             };
+
+    private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
+            (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 567d0cb..72da7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -256,13 +256,13 @@
         // Force animations when transitioning from a dialog to an activity
         intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
 
-        if (keyguardStateController.isUnlocked()) {
+        if (keyguardStateController.isShowing()) {
+            activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
+        } else {
             activityContext.startActivity(
                 intent,
                 ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
             )
-        } else {
-            activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 657d924..845d7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -275,6 +275,13 @@
     }
 
     /**
+     * Appends sensor event dropped event to logs
+     */
+    public void traceSensorEventDropped(int sensorEvent, String reason) {
+        mLogger.logSensorEventDropped(sensorEvent, reason);
+    }
+
+    /**
      * Appends pulse dropped event to logs
      * @param reason why the pulse was dropped
      */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index fe37d49..dc18618 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -205,6 +205,15 @@
         })
     }
 
+    fun logSensorEventDropped(sensorEvent: Int, reason: String) {
+        buffer.log(TAG, INFO, {
+            int1 = sensorEvent
+            str1 = reason
+        }, {
+            "SensorEvent [$int1] dropped, reason=$str1"
+        })
+    }
+
     fun logPulseDropped(reason: String) {
         buffer.log(TAG, INFO, {
             str1 = reason
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 455f3c0..756adca 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
 import com.android.systemui.doze.DozeMachine.State;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -92,6 +93,7 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final AuthController mAuthController;
     private final DelayableExecutor mMainExecutor;
+    private final KeyguardStateController mKeyguardStateController;
     private final UiEventLogger mUiEventLogger;
 
     private long mNotificationPulseTime;
@@ -179,7 +181,8 @@
             DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
             SecureSettings secureSettings, AuthController authController,
             @Main DelayableExecutor mainExecutor,
-            UiEventLogger uiEventLogger) {
+            UiEventLogger uiEventLogger,
+            KeyguardStateController keyguardStateController) {
         mContext = context;
         mDozeHost = dozeHost;
         mConfig = config;
@@ -198,6 +201,7 @@
         mAuthController = authController;
         mMainExecutor = mainExecutor;
         mUiEventLogger = uiEventLogger;
+        mKeyguardStateController = keyguardStateController;
     }
 
     @Override
@@ -294,6 +298,7 @@
             proximityCheckThenCall((result) -> {
                 if (result != null && result) {
                     // In pocket, drop event.
+                    mDozeLog.traceSensorEventDropped(pulseReason, "prox reporting near");
                     return;
                 }
                 if (isDoubleTap || isTap) {
@@ -302,6 +307,10 @@
                     }
                     gentleWakeUp(pulseReason);
                 } else if (isPickup) {
+                    if (shouldDropPickupEvent())  {
+                        mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
+                        return;
+                    }
                     gentleWakeUp(pulseReason);
                 } else if (isUdfpsLongPress) {
                     final State state = mMachine.getState();
@@ -320,7 +329,7 @@
             }, true /* alreadyPerformedProxCheck */, pulseReason);
         }
 
-        if (isPickup) {
+        if (isPickup && !shouldDropPickupEvent()) {
             final long timeSinceNotification =
                     SystemClock.elapsedRealtime() - mNotificationPulseTime;
             final boolean withinVibrationThreshold =
@@ -329,6 +338,10 @@
         }
     }
 
+    private boolean shouldDropPickupEvent() {
+        return mKeyguardStateController.isOccluded();
+    }
+
     private void gentleWakeUp(int reason) {
         // Log screen wake up reason (lift/pickup, tap, double-tap)
         Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index 2d215e0..a424674 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -100,8 +100,7 @@
      */
     public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
             boolean updateImmediately) {
-        if (type == INDICATION_TYPE_NOW_PLAYING
-                || type == INDICATION_TYPE_REVERSE_CHARGING) {
+        if (type == INDICATION_TYPE_REVERSE_CHARGING) {
             // temporarily don't show here, instead use AmbientContainer b/181049781
             return;
         }
@@ -303,7 +302,6 @@
     public static final int INDICATION_TYPE_TRUST = 6;
     public static final int INDICATION_TYPE_RESTING = 7;
     public static final int INDICATION_TYPE_USER_LOCKED = 8;
-    public static final int INDICATION_TYPE_NOW_PLAYING = 9;
     public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
 
     @IntDef({
@@ -317,7 +315,6 @@
             INDICATION_TYPE_TRUST,
             INDICATION_TYPE_RESTING,
             INDICATION_TYPE_USER_LOCKED,
-            INDICATION_TYPE_NOW_PLAYING,
             INDICATION_TYPE_REVERSE_CHARGING,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index db5dbb0..c743fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -772,7 +772,8 @@
                     31, // MEDIA_RESUME
                 uid,
                 interactedSubcardRank,
-                interactedSubcardCardinality
+                interactedSubcardCardinality,
+                0 // received_latency_millis
         )
         /* ktlint-disable max-line-length */
     }
@@ -857,7 +858,8 @@
     ) {
         shouldPrioritizeSs = shouldPrioritize
         removeMediaPlayer(key)
-        val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, clock.currentTimeMillis())
+        val sortKey = MediaSortKey(/* isSsMediaRec= */ true,
+            EMPTY.copy(isPlaying = false), clock.currentTimeMillis())
         mediaData.put(key, sortKey)
         mediaPlayers.put(sortKey, player)
         smartspaceMediaData = data
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c125612..e7445f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -75,6 +75,8 @@
     private static final String TAG = "MediaControlPanel";
 
     private static final float DISABLED_ALPHA = 0.38f;
+    private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google"
+            + ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity";
     private static final String EXTRAS_SMARTSPACE_INTENT =
             "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
     private static final int MEDIA_RECOMMENDATION_ITEMS_PER_ROW = 3;
@@ -252,7 +254,8 @@
                 openGuts();
                 return true;
             } else {
-                return false;
+                closeGuts();
+                return true;
             }
         });
         mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
@@ -585,6 +588,14 @@
             ViewGroup mediaCoverContainer = mediaCoverContainers.get(uiComponentIndex);
             setSmartspaceRecItemOnClickListener(mediaCoverContainer, recommendation,
                     uiComponentIndex);
+            // Bubble up the long-click event to the card.
+            mediaCoverContainer.setOnLongClickListener(v -> {
+                View parent = (View) v.getParent();
+                if (parent != null) {
+                    parent.performLongClick();
+                }
+                return true;
+            });
 
             // Set up the accessibility label for the media item.
             String artistName = recommendation.getExtras()
@@ -627,6 +638,22 @@
             closeGuts();
             mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
                     data.getTargetId(), MediaViewController.GUTS_ANIMATION_DURATION + 100L);
+
+            Intent dismissIntent = data.getDismissIntent();
+            if (dismissIntent == null) {
+                Log.w(TAG, "Cannot create dismiss action click action: "
+                        + "extras missing dismiss_intent.");
+                return;
+            }
+
+            if (dismissIntent.getComponent() != null
+                    && dismissIntent.getComponent().getClassName()
+                    .equals(EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)) {
+                // Dismiss the card Smartspace data through Smartspace trampoline activity.
+                mContext.startActivity(dismissIntent);
+            } else {
+                mContext.sendBroadcast(dismissIntent);
+            }
         });
 
         mController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index c8deb01..79206e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media
 
+import android.content.Context
 import android.os.SystemProperties
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
@@ -32,6 +33,8 @@
 
 private const val TAG = "MediaDataFilter"
 private const val DEBUG = true
+private const val EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = ("com.google" +
+        ".android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity")
 private const val RESUMABLE_MEDIA_MAX_AGE_SECONDS_KEY = "resumable_media_max_age_seconds"
 
 /**
@@ -51,6 +54,7 @@
  * background users (e.g. timeouts).
  */
 class MediaDataFilter @Inject constructor(
+    private val context: Context,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val mediaResumeListener: MediaResumeListener,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -229,6 +233,18 @@
             mediaDataManager.setTimedOut(it, timedOut = true, forceUpdate = true)
         }
         if (smartspaceMediaData.isActive) {
+            val dismissIntent = smartspaceMediaData.dismissIntent
+            if (dismissIntent == null) {
+                Log.w(TAG, "Cannot create dismiss action click action: " +
+                        "extras missing dismiss_intent.")
+            } else if (dismissIntent.getComponent() != null &&
+                    dismissIntent.getComponent().getClassName()
+                    == EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME) {
+                // Dismiss the card Smartspace data through Smartspace trampoline activity.
+                context.startActivity(dismissIntent)
+            } else {
+                context.sendBroadcast(dismissIntent)
+            }
             smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
                 targetId = smartspaceMediaData.targetId, isValid = smartspaceMediaData.isValid)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index ba99f5d..eacdab6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -76,12 +76,13 @@
 
 private const val TAG = "MediaDataManager"
 private const val DEBUG = true
+private const val EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY = "dismiss_intent"
 
 private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
         emptyList(), emptyList(), "INVALID", null, null, null, true, null)
 @VisibleForTesting
 internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
-    "INVALID", null, emptyList(), 0)
+    "INVALID", null, emptyList(), null, 0)
 
 fun isMediaNotification(sbn: StatusBarNotification): Boolean {
     if (!sbn.notification.hasMediaSession()) {
@@ -883,12 +884,22 @@
         target: SmartspaceTarget,
         isActive: Boolean
     ): SmartspaceMediaData {
+        var dismissIntent: Intent? = null
+        if (target.baseAction != null && target.baseAction.extras != null) {
+            dismissIntent = target
+                .baseAction
+                .extras
+                .getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent
+        }
         packageName(target)?.let {
             return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
-                target.baseAction, target.iconGrid, 0)
+                target.baseAction, target.iconGrid,
+                dismissIntent, 0)
         }
         return EMPTY_SMARTSPACE_MEDIA_DATA
-            .copy(targetId = target.smartspaceTargetId, isActive = isActive)
+            .copy(targetId = target.smartspaceTargetId,
+                isActive = isActive,
+                dismissIntent = dismissIntent)
     }
 
     private fun packageName(target: SmartspaceTarget): String? {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 9ac1289..61fdefd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media
 
 import android.app.smartspace.SmartspaceAction
+import android.content.Intent
 
 /** State of a Smartspace media recommendations view. */
 data class SmartspaceMediaData(
@@ -45,6 +46,10 @@
      */
     val recommendations: List<SmartspaceAction>,
     /**
+     * Intent for the user's initiated dismissal.
+     */
+    val dismissIntent: Intent?,
+    /**
      * View's background color.
      */
     val backgroundColor: Int
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
index b6c2ef1..140a1fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
@@ -1,10 +1,13 @@
 package com.android.systemui.media
 
 import android.app.smartspace.SmartspaceTarget
+import android.util.Log
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
 import javax.inject.Inject
 
+private const val TAG = "SsMediaDataProvider"
+
 /** Provides SmartspaceTargets of media types for SystemUI media control. */
 class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin {
 
@@ -31,6 +34,10 @@
             }
         }
 
+        if (!mediaTargets.isEmpty()) {
+            Log.d(TAG, "Forwarding Smartspace media updates $mediaTargets")
+        }
+
         smartspaceMediaTargets = mediaTargets
         smartspaceMediaTargetListeners.forEach {
             it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 43315f6..8576a28 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -712,6 +712,7 @@
         mHandler.removeCallbacks(mAutoDim);
         mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
         mHandler.removeCallbacks(mEnableLayoutTransitions);
+        mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
         mFrame = null;
         mNavigationBarView = null;
         mOrientationHandle = null;
@@ -1391,10 +1392,11 @@
     void updateAccessibilityServicesState() {
         int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
 
-        boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
-        boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
-        mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
-
+        if (mNavigationBarView != null) {
+            boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+            boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+            mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
+        }
         updateSystemUiStateFlags(a11yFlags);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b8df49e..a1a630a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -205,7 +205,7 @@
     @Override
     public void onConfigChanged(Configuration newConfig) {
         boolean isOldConfigTablet = mIsTablet;
-        mIsTablet = isTablet(newConfig, mContext);
+        mIsTablet = isTablet(mContext);
         boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
         // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
         if (largeScreenChanged && updateNavbarForTaskbar()) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index ecc3245..71d2a73 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -96,14 +96,17 @@
         mOverviewProxyService.removeCallback(this);
         mNavigationModeController.removeListener(this);
         mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+        mEdgeBackGestureHandler.onNavBarDetached();
     }
 
     public void init(int displayId) {
         mDisplayId = displayId;
         mCommandQueue.addCallback(this);
         mOverviewProxyService.addCallback(this);
-        mNavigationModeController.addListener(this);
+        mEdgeBackGestureHandler.onNavigationModeChanged(
+                mNavigationModeController.addListener(this));
         mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+        mEdgeBackGestureHandler.onNavBarAttached();
         // Set initial state for any listeners
         updateSysuiFlags();
     }
@@ -179,15 +182,6 @@
     }
 
     @Override
-    public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
-        if (visible) {
-            mEdgeBackGestureHandler.onNavBarAttached();
-        } else {
-            mEdgeBackGestureHandler.onNavBarDetached();
-        }
-    }
-
-    @Override
     public void onNavigationModeChanged(int mode) {
         mEdgeBackGestureHandler.onNavigationModeChanged(mode);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1bd3664..1a7a306 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -114,6 +114,7 @@
         super.onConfigurationChanged(newConfig);
         if (mLayoutOrientation != newConfig.orientation) {
             mLayoutOrientation = newConfig.orientation;
+            mDistributeTiles = true;
             setCurrentItem(0, false);
             mPageToRestore = 0;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 8659b8b..bfb63ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -64,7 +64,10 @@
     // Fade out faster than fade in to finish before QQS hides.
     private static final long QQS_FADE_OUT_DURATION = 50L;
 
-
+    /**
+     * List of all views that will be reset when clearing animation state
+     * see {@link #clearAnimationState()} }
+     */
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
      * List of {@link View}s representing Quick Settings that are being animated from the quick QS
@@ -397,6 +400,7 @@
                     tileView.setClipChildren(true);
                     tileView.setClipToPadding(true);
                     firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
+                    mAllViews.add(tileView.getSecondaryLabel());
                 }
 
                 mAllViews.add(tileView);
@@ -405,21 +409,8 @@
         }
 
         if (mAllowFancy) {
-            // Make brightness appear static position and alpha in through second half.
-            View brightness = mQsPanelController.getBrightnessView();
-            if (brightness != null) {
-                firstPageBuilder.addFloat(brightness, "translationY",
-                        brightness.getMeasuredHeight() * 0.5f, 0);
-                mBrightnessAnimator = new TouchAnimator.Builder()
-                        .addFloat(brightness, "alpha", 0, 1)
-                        .addFloat(brightness, "sliderScaleY", 0.3f, 1)
-                        .setInterpolator(Interpolators.ALPHA_IN)
-                        .setStartDelay(0.3f)
-                        .build();
-                mAllViews.add(brightness);
-            } else {
-                mBrightnessAnimator = null;
-            }
+            animateBrightnessSlider(firstPageBuilder);
+
             mFirstPageAnimator = firstPageBuilder
                     .setListener(this)
                     .build();
@@ -470,20 +461,53 @@
                 .addFloat(tileLayout, "alpha", 0, 1).build();
     }
 
+    private void animateBrightnessSlider(Builder firstPageBuilder) {
+        View qsBrightness = mQsPanelController.getBrightnessView();
+        View qqsBrightness = mQuickQSPanelController.getBrightnessView();
+        if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) {
+            // animating in split shade mode
+            mAnimatedQsViews.add(qsBrightness);
+            mAllViews.add(qqsBrightness);
+            int translationY = getRelativeTranslationY(qsBrightness, qqsBrightness);
+            mBrightnessAnimator = new Builder()
+                    // we need to animate qs brightness even if animation will not be visible,
+                    // as we might start from sliderScaleY set to 0.3 if device was in collapsed QS
+                    // portrait orientation before
+                    .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
+                    .addFloat(qqsBrightness, "translationY", 0, translationY)
+                    .build();
+        } else if (qsBrightness != null) {
+            firstPageBuilder.addFloat(qsBrightness, "translationY",
+                    qsBrightness.getMeasuredHeight() * 0.5f, 0);
+            mBrightnessAnimator = new Builder()
+                    .addFloat(qsBrightness, "alpha", 0, 1)
+                    .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .setStartDelay(0.3f)
+                    .build();
+            mAllViews.add(qsBrightness);
+        } else {
+            mBrightnessAnimator = null;
+        }
+    }
+
     private void updateQQSFooterAnimation() {
-        int[] qsPosition = new int[2];
-        int[] qqsPosition = new int[2];
-        View commonView = mQs.getView();
-        getRelativePositionInt(qsPosition, mQSFooterActions, commonView);
-        getRelativePositionInt(qqsPosition, mQQSFooterActions, commonView);
-        int translationY = (qsPosition[1] - qqsPosition[1])
-                - mQuickStatusBarHeader.getOffsetTranslation();
+        int translationY = getRelativeTranslationY(mQSFooterActions, mQQSFooterActions);
         mQQSFooterActionsAnimator = new TouchAnimator.Builder()
                 .addFloat(mQQSFooterActions, "translationY", 0, translationY)
                 .build();
         mAnimatedQsViews.add(mQSFooterActions);
     }
 
+    private int getRelativeTranslationY(View view1, View view2) {
+        int[] qsPosition = new int[2];
+        int[] qqsPosition = new int[2];
+        View commonView = mQs.getView();
+        getRelativePositionInt(qsPosition, view1, commonView);
+        getRelativePositionInt(qqsPosition, view2, commonView);
+        return (qsPosition[1] - qqsPosition[1]) - mQuickStatusBarHeader.getOffsetTranslation();
+    }
+
     private boolean isIconInAnimatedRow(int count) {
         if (mPagedLayout == null) {
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index a128694..8e43661 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.Path;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.WindowInsets;
@@ -208,9 +209,17 @@
 
     protected int calculateContainerHeight() {
         int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+        // Need to add the dragHandle height so touches will be intercepted by it.
+        int dragHandleHeight;
+        if (mDragHandle.getVisibility() == VISIBLE) {
+            dragHandleHeight = Math.round((1 - mQsExpansion) * mDragHandle.getHeight());
+        } else {
+            dragHandleHeight = 0;
+        }
         return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
                 : Math.round(mQsExpansion * (heightOverride - mHeader.getHeight()))
-                + mHeader.getHeight();
+                + mHeader.getHeight()
+                + dragHandleHeight;
     }
 
     int calculateContainerBottom() {
@@ -226,6 +235,7 @@
         mQsExpansion = expansion;
         mQSPanelContainer.setScrollingEnabled(expansion > 0f);
         mDragHandle.setAlpha(1.0f - expansion);
+        mDragHandle.setClickable(expansion == 0f); // Only clickable when fully collapsed
         updateExpansion();
     }
 
@@ -295,6 +305,16 @@
         }
     }
 
+    @Override
+    protected boolean isTransformedTouchPointInView(float x, float y,
+            View child, PointF outLocalPoint) {
+        // Prevent touches outside the clipped area from propagating to a child in that area.
+        if (mClippingEnabled && y + getTranslationY() > mFancyClippingTop) {
+            return false;
+        }
+        return super.isTransformedTouchPointInView(x, y, child, outLocalPoint);
+    }
+
     private void updateClippingPath() {
         mFancyClippingPath.reset();
         if (!mClippingEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 4242e1b..6fd8141 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -37,7 +37,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.dump.DumpManager;
@@ -238,6 +237,11 @@
                     mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f);
                     mQSAnimator.requestAnimatorUpdate();
                 });
+
+        mQsDragHandler.setOnClickListener(v -> {
+            Log.d(TAG, "drag handler clicked");
+            mCommandQueue.animateExpandSettingsPanel(null);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 9025427..70892a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -258,10 +258,6 @@
         return mView.isLayoutRtl();
     }
 
-    public View getBrightnessView() {
-        return mView.getBrightnessView();
-    }
-
     /** */
     public void setPageListener(PagedTileLayout.PageListener listener) {
         mView.setPageListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 42323e3..f7d1b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -24,7 +24,9 @@
 import android.content.ComponentName;
 import android.content.res.Configuration;
 import android.metrics.LogMaker;
+import android.view.View;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.Dumpable;
@@ -78,7 +80,8 @@
 
     private final QSHost.Callback mQSHostCallback = this::setTiles;
 
-    private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
+    @VisibleForTesting
+    protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
                 @Override
                 public void onConfigurationChange(Configuration newConfig) {
@@ -155,6 +158,7 @@
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
         mHost.addCallback(mQSHostCallback);
         setTiles();
+        mLastOrientation = getResources().getConfiguration().orientation;
         switchTileLayout(true);
 
         mDumpManager.registerDumpable(mView.getDumpableTag(), this);
@@ -355,8 +359,7 @@
             return false;
         }
         return mUsingMediaPlayer && mMediaHost.getVisible()
-                    && getResources().getConfiguration().orientation
-                    == Configuration.ORIENTATION_LANDSCAPE;
+                && mLastOrientation == Configuration.ORIENTATION_LANDSCAPE;
     }
 
     private void logTiles() {
@@ -405,6 +408,10 @@
         mUsingHorizontalLayoutChangedListener = listener;
     }
 
+    public View getBrightnessView() {
+        return mView.getBrightnessView();
+    }
+
     /** */
     public static final class TileRecord extends QSPanel.Record {
         public QSTile tile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 359f3a4..a81d3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -280,6 +280,25 @@
         updateBatteryMode();
         updateHeadersPadding();
         updateAnimators();
+
+        updateClockDatePadding();
+    }
+
+    private void updateClockDatePadding() {
+        int startPadding = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding);
+        int endPadding = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding);
+        mClockView.setPaddingRelative(
+                startPadding,
+                mClockView.getPaddingTop(),
+                endPadding,
+                mClockView.getPaddingBottom()
+        );
+
+        MarginLayoutParams lp = (MarginLayoutParams) mClockDateView.getLayoutParams();
+        lp.setMarginStart(endPadding);
+        mClockDateView.setLayoutParams(lp);
     }
 
     private void updateAnimators() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 73d1370..14e0f70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -54,7 +54,7 @@
     private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
     private val icon = ResourceIcon.get(R.drawable.ic_alarm)
     @VisibleForTesting
-    internal val defaultIntent = Intent(AlarmClock.ACTION_SET_ALARM)
+    internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
     private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
         lastAlarmInfo = nextAlarm
         refreshState()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 8e886e8..b1af841 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -275,7 +275,7 @@
                 return;
             }
             mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
-            mInfo.dataContentDescription = indicators.description != null
+            mInfo.dataContentDescription = indicators.qsDescription != null
                     ? indicators.typeContentDescriptionHtml : null;
             mInfo.activityIn = indicators.activityIn;
             mInfo.activityOut = indicators.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 34f2b63..87edc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -47,6 +47,7 @@
 
 /** Quick settings tile: Hotspot **/
 public class HotspotTile extends QSTileImpl<BooleanState> {
+
     private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
 
     private final HotspotController mHotspotController;
@@ -98,7 +99,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_TETHER_SETTINGS);
+        return new Intent(Settings.ACTION_WIFI_TETHER_SETTING);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cc9e748..530804ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -279,9 +279,9 @@
                 // Not data sim, don't display.
                 return;
             }
-            mCellularInfo.mDataSubscriptionName = indicators.description == null
-                    ? mController.getMobileDataNetworkName() : indicators.description;
-            mCellularInfo.mDataContentDescription = indicators.description != null
+            mCellularInfo.mDataSubscriptionName = indicators.qsDescription == null
+                    ? mController.getMobileDataNetworkName() : indicators.qsDescription;
+            mCellularInfo.mDataContentDescription = indicators.qsDescription != null
                     ? indicators.typeContentDescriptionHtml : null;
             mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon;
             mCellularInfo.mQsTypeIcon = indicators.qsType;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index b1db8a9..dc54e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -45,13 +45,14 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
-import android.widget.Button;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.Switch;
 import android.widget.TextView;
 
+import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
@@ -60,7 +61,6 @@
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.Utils;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
@@ -105,12 +105,10 @@
     private View mDivider;
     private ProgressBar mProgressBar;
     private LinearLayout mInternetDialogLayout;
-    private LinearLayout mInternetListLayout;
     private LinearLayout mConnectedWifListLayout;
-    private LinearLayout mConnectedWifList;
     private LinearLayout mMobileNetworkLayout;
-    private LinearLayout mMobileNetworkList;
     private LinearLayout mTurnWifiOnLayout;
+    private LinearLayout mEthernetLayout;
     private TextView mWifiToggleTitleText;
     private LinearLayout mSeeAllLayout;
     private RecyclerView mWifiRecyclerView;
@@ -123,10 +121,9 @@
     private TextView mMobileSummaryText;
     private Switch mMobileDataToggle;
     private Switch mWiFiToggle;
-    private Button mDoneButton;
+    private FrameLayout mDoneLayout;
     private Drawable mBackgroundOn;
     private int mListMaxHeight;
-    private int mLayoutWidth;
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private boolean mCanConfigMobileData;
 
@@ -181,8 +178,6 @@
         };
         mListMaxHeight = context.getResources().getDimensionPixelSize(
                 R.dimen.internet_dialog_list_max_height);
-        mLayoutWidth = context.getResources().getDimensionPixelSize(
-                R.dimen.internet_dialog_list_max_width);
         mUiEventLogger = uiEventLogger;
         mAdapter = new InternetAdapter(mInternetDialogController);
         if (!aboveStatusBar) {
@@ -221,20 +216,18 @@
         mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
         mDivider = mDialogView.requireViewById(R.id.divider);
         mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
-        mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+        mEthernetLayout = mDialogView.requireViewById(R.id.ethernet_layout);
         mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
-        mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
         mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
         mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
         mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
-        mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
         mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
         mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
         mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
         mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
         mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
         mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
-        mDoneButton = mDialogView.requireViewById(R.id.done);
+        mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
         mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
         mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
         mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
@@ -286,7 +279,7 @@
         mConnectedWifListLayout.setOnClickListener(null);
         mSeeAllLayout.setOnClickListener(null);
         mWiFiToggle.setOnCheckedChangeListener(null);
-        mDoneButton.setOnClickListener(null);
+        mDoneLayout.setOnClickListener(null);
         mInternetDialogController.onStop();
         mInternetDialogFactory.destroyDialog();
     }
@@ -309,7 +302,9 @@
         } else {
             mInternetDialogSubTitle.setText(getSubtitleText());
         }
-        setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+        updateEthernet();
+        setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
+                || mInternetDialogController.isCarrierNetworkActive());
 
         if (!mCanConfigWifi) {
             return;
@@ -352,10 +347,16 @@
                     buttonView.setChecked(isChecked);
                     mWifiManager.setWifiEnabled(isChecked);
                 });
-        mDoneButton.setOnClickListener(v -> dismiss());
+        mDoneLayout.setOnClickListener(v -> dismiss());
     }
 
-    private void setMobileDataLayout(boolean isCellularNetwork) {
+    @MainThread
+    private void updateEthernet() {
+        mEthernetLayout.setVisibility(
+                mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
+    }
+
+    private void setMobileDataLayout(boolean isCarrierNetworkConnected) {
         if (mInternetDialogController.isAirplaneModeEnabled()
                 || !mInternetDialogController.hasCarrier()) {
             mMobileNetworkLayout.setVisibility(View.GONE);
@@ -371,38 +372,33 @@
                 mMobileSummaryText.setVisibility(View.GONE);
             }
             mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
-            if (mInternetDialogController.isNightMode()) {
-                int titleColor = isCellularNetwork ? mContext.getColor(
-                        R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
-                        mContext, android.R.attr.textColorPrimary);
-                int summaryColor = isCellularNetwork ? mContext.getColor(
-                        R.color.connected_network_secondary_color) : Utils.getColorAttrDefaultColor(
-                        mContext, android.R.attr.textColorSecondary);
-
-                mMobileTitleText.setTextColor(titleColor);
-                mMobileSummaryText.setTextColor(summaryColor);
-            }
-            mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+            mMobileTitleText.setTextAppearance(isCarrierNetworkConnected
+                    ? R.style.TextAppearance_InternetDialog_Active
+                    : R.style.TextAppearance_InternetDialog);
+            mMobileSummaryText.setTextAppearance(isCarrierNetworkConnected
+                    ? R.style.TextAppearance_InternetDialog_Secondary_Active
+                    : R.style.TextAppearance_InternetDialog_Secondary);
+            mMobileNetworkLayout.setBackground(isCarrierNetworkConnected ? mBackgroundOn : null);
 
             mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
         }
     }
 
+    @MainThread
     private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
         mWiFiToggle.setChecked(isWifiEnabled);
-        if (isDeviceLocked && mInternetDialogController.isNightMode()) {
-            int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
-                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
-                    mContext, android.R.attr.textColorPrimary);
-            mWifiToggleTitleText.setTextColor(titleColor);
+        if (isDeviceLocked) {
+            mWifiToggleTitleText.setTextAppearance((mConnectedWifiEntry != null)
+                    ? R.style.TextAppearance_InternetDialog_Active
+                    : R.style.TextAppearance_InternetDialog);
         }
         mTurnWifiOnLayout.setBackground(
                 (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
     }
 
+    @MainThread
     private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
         if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
-            mConnectedWifListLayout.setBackground(null);
             mConnectedWifListLayout.setVisibility(View.GONE);
             return;
         }
@@ -411,15 +407,8 @@
         mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
         mConnectedWifiIcon.setImageDrawable(
                 mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
-        if (mInternetDialogController.isNightMode()) {
-            mConnectedWifiTitleText.setTextColor(
-                    mContext.getColor(R.color.connected_network_primary_color));
-            mConnectedWifiSummaryText.setTextColor(
-                    mContext.getColor(R.color.connected_network_secondary_color));
-        }
         mWifiSettingsIcon.setColorFilter(
                 mContext.getColor(R.color.connected_network_primary_color));
-        mConnectedWifListLayout.setBackground(mBackgroundOn);
     }
 
     void onClickConnectedWifi() {
@@ -528,11 +517,18 @@
     }
 
     @Override
+    @WorkerThread
     public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
         mHandler.post(() -> updateDialog());
     }
 
     @Override
+    @WorkerThread
+    public void onLost(Network network) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
     public void onSubscriptionsChanged(int defaultDataSubId) {
         mDefaultDataSubId = defaultDataSubId;
         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 50e7e43..aaba5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -19,21 +19,24 @@
 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.PixelFormat;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.NetworkRegistrationInfo;
@@ -47,8 +50,10 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
-import android.widget.Toast;
+import android.view.View;
+import android.view.WindowManager;
 
+import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -67,11 +72,15 @@
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.toast.SystemUIToast;
+import com.android.systemui.toast.ToastFactory;
+import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
@@ -117,10 +126,12 @@
     private SubscriptionManager mSubscriptionManager;
     private TelephonyManager mTelephonyManager;
     private ConnectivityManager mConnectivityManager;
+    private CarrierConfigTracker mCarrierConfigTracker;
     private TelephonyDisplayInfo mTelephonyDisplayInfo =
             new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
     private Handler mHandler;
+    private Handler mWorkerHandler;
     private MobileMappings.Config mConfig = null;
     private Executor mExecutor;
     private AccessPointController mAccessPointController;
@@ -133,8 +144,17 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private GlobalSettings mGlobalSettings;
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
+    private WindowManager mWindowManager;
+    private ToastFactory mToastFactory;
 
     @VisibleForTesting
+    static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
+    @VisibleForTesting
+    static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
+    @VisibleForTesting
+    static final long SHORT_DURATION_TIMEOUT = 4000;
+    @VisibleForTesting
     protected ActivityStarter mActivityStarter;
     @VisibleForTesting
     protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@@ -146,6 +166,8 @@
     protected boolean mCanConfigWifi;
     @VisibleForTesting
     protected KeyguardStateController mKeyguardStateController;
+    @VisibleForTesting
+    protected boolean mHasEthernet = false;
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -171,11 +193,15 @@
             @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
             @Main Handler handler, @Main Executor mainExecutor,
             BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
-            GlobalSettings globalSettings, KeyguardStateController keyguardStateController) {
+            GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
+            WindowManager windowManager, ToastFactory toastFactory,
+            @Background Handler workerHandler,
+            CarrierConfigTracker carrierConfigTracker) {
         if (DEBUG) {
             Log.d(TAG, "Init InternetDialogController");
         }
         mHandler = handler;
+        mWorkerHandler = workerHandler;
         mExecutor = mainExecutor;
         mContext = context;
         mGlobalSettings = globalSettings;
@@ -183,16 +209,20 @@
         mTelephonyManager = telephonyManager;
         mConnectivityManager = connectivityManager;
         mSubscriptionManager = subscriptionManager;
+        mCarrierConfigTracker = carrierConfigTracker;
         mBroadcastDispatcher = broadcastDispatcher;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardStateController = keyguardStateController;
         mConnectionStateFilter = new IntentFilter();
         mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         mUiEventLogger = uiEventLogger;
         mActivityStarter = starter;
         mAccessPointController = accessPointController;
-        mConfig = MobileMappings.Config.readConfig(mContext);
         mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
+        mConnectivityManagerNetworkCallback = new DataConnectivityListener();
+        mWindowManager = windowManager;
+        mToastFactory = toastFactory;
     }
 
     void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -212,13 +242,12 @@
         if (DEBUG) {
             Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
         }
+        mConfig = MobileMappings.Config.readConfig(mContext);
         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
         mInternetTelephonyCallback = new InternetTelephonyCallback();
         mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
         // Listen the connectivity changes
-        mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .build(), new DataConnectivityListener(), mHandler);
+        mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
         mCanConfigWifi = canConfigWifi;
         scanWifiAccessPoints();
     }
@@ -233,6 +262,7 @@
                 mOnSubscriptionsChangedListener);
         mAccessPointController.removeAccessPointCallback(this);
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+        mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
     }
 
     @VisibleForTesting
@@ -351,11 +381,6 @@
         return drawable;
     }
 
-    boolean isNightMode() {
-        return (mContext.getResources().getConfiguration().uiMode
-                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
-    }
-
     Drawable getSignalStrengthDrawable() {
         Drawable drawable = mContext.getDrawable(
                 R.drawable.ic_signal_strength_zero_bar_no_internet);
@@ -373,9 +398,12 @@
                 drawable = shared.get();
             }
 
-            drawable.setTint(activeNetworkIsCellular() ? mContext.getColor(
-                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
-                    mContext, android.R.attr.textColorTertiary));
+            int tintColor = Utils.getColorAttrDefaultColor(mContext,
+                    android.R.attr.textColorTertiary);
+            if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
+                tintColor = mContext.getColor(R.color.connected_network_primary_color);
+            }
+            drawable.setTint(tintColor);
         } catch (Throwable e) {
             e.printStackTrace();
         }
@@ -518,7 +546,7 @@
     String getMobileNetworkSummary() {
         String description = getNetworkTypeDescription(mContext, mConfig,
                 mTelephonyDisplayInfo, mDefaultDataSubId);
-        return getMobileSummary(mContext, mTelephonyManager, description);
+        return getMobileSummary(mContext, description);
     }
 
     /**
@@ -536,9 +564,7 @@
         }
 
         int resId = mapIconSets(config).get(iconKey).dataContentDescription;
-        final MergedCarrierEntry mergedCarrierEntry =
-                mAccessPointController.getMergedCarrierEntry();
-        if (mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork()) {
+        if (isCarrierNetworkActive()) {
             SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
                     TelephonyIcons.CARRIER_MERGED_WIFI;
             resId = carrierMergedWifiIconGroup.dataContentDescription;
@@ -548,8 +574,7 @@
                 ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
     }
 
-    private String getMobileSummary(Context context, TelephonyManager telephonyManager,
-            String networkTypeDescription) {
+    private String getMobileSummary(Context context, String networkTypeDescription) {
         if (!isMobileDataEnabled()) {
             return context.getString(R.string.mobile_data_off_summary);
         }
@@ -557,7 +582,7 @@
             return context.getString(R.string.mobile_data_no_connection);
         }
         String summary = networkTypeDescription;
-        if (activeNetworkIsCellular()) {
+        if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
             summary = context.getString(R.string.preference_summary_default_combination,
                     context.getString(R.string.mobile_data_connection_active),
                     networkTypeDescription);
@@ -582,10 +607,35 @@
         final MergedCarrierEntry mergedCarrierEntry =
                 mAccessPointController.getMergedCarrierEntry();
         if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
-            mergedCarrierEntry.connect(null /* ConnectCallback */);
+            mergedCarrierEntry.connect(null /* ConnectCallback */, false);
+            makeOverlayToast(R.string.wifi_wont_autoconnect_for_now);
         }
     }
 
+    boolean isCarrierNetworkActive() {
+        final MergedCarrierEntry mergedCarrierEntry =
+                mAccessPointController.getMergedCarrierEntry();
+        return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();
+    }
+
+    @WorkerThread
+    void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) {
+        // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier
+        // Wi-Fi state together.
+        if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) {
+            return;
+        }
+
+        final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry();
+        if (entry == null) {
+            if (DEBUG) {
+                Log.d(TAG, "MergedCarrierEntry is null, can not set the status.");
+            }
+            return;
+        }
+        entry.setEnabled(enabled);
+    }
+
     WifiManager getWifiManager() {
         return mWifiManager;
     }
@@ -660,6 +710,7 @@
                 }
             }
         }
+        mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled));
     }
 
     boolean isDataStateInService() {
@@ -725,20 +776,20 @@
                 Log.d(TAG, "connect to unsaved network " + ap.getTitle());
             }
         }
-        ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+        ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this));
         return false;
     }
 
     static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
         final ActivityStarter mActivityStarter;
-        final Context mContext;
         final WifiEntry mWifiEntry;
+        final InternetDialogController mInternetDialogController;
 
-        WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
-                WifiEntry connectWifiEntry) {
+        WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry,
+                InternetDialogController internetDialogController) {
             mActivityStarter = activityStarter;
-            mContext = context;
             mWifiEntry = connectWifiEntry;
+            mInternetDialogController = internetDialogController;
         }
 
         @Override
@@ -753,8 +804,7 @@
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 mActivityStarter.startActivity(intent, false /* dismissShade */);
             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
-                Toast.makeText(mContext, R.string.wifi_failed_connect_message,
-                        Toast.LENGTH_SHORT).show();
+                mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "connect failure reason=" + status);
@@ -800,6 +850,9 @@
         }
 
         int count = MAX_WIFI_ENTRY_COUNT;
+        if (mHasEthernet) {
+            count -= 1;
+        }
         if (hasCarrier()) {
             count -= 1;
         }
@@ -870,31 +923,43 @@
         @Override
         @WorkerThread
         public void onCapabilitiesChanged(@NonNull Network network,
-                @NonNull NetworkCapabilities networkCapabilities) {
-            if (mCanConfigWifi) {
-                for (int transport : networkCapabilities.getTransportTypes()) {
-                    if (transport == NetworkCapabilities.TRANSPORT_WIFI) {
-                        scanWifiAccessPoints();
-                        break;
-                    }
-                }
+                @NonNull NetworkCapabilities capabilities) {
+            mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
+            if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport(
+                    NetworkCapabilities.TRANSPORT_WIFI))) {
+                scanWifiAccessPoints();
             }
-            final Network activeNetwork = mConnectivityManager.getActiveNetwork();
-            if (activeNetwork != null && activeNetwork.equals(network)) {
-                // update UI
-                mCallback.onCapabilitiesChanged(network, networkCapabilities);
-            }
+            // update UI
+            mCallback.onCapabilitiesChanged(network, capabilities);
         }
+
+        @Override
+        @WorkerThread
+        public void onLost(@NonNull Network network) {
+            mHasEthernet = false;
+            mCallback.onLost(network);
+        }
+    }
+
+    /**
+     * Return {@code true} If the Ethernet exists
+     */
+    @MainThread
+    public boolean hasEthernet() {
+        return mHasEthernet;
     }
 
     private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+            if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
                 if (DEBUG) {
                     Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
                 }
+                mConfig = MobileMappings.Config.readConfig(context);
+                updateListener();
+            } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) {
                 updateListener();
             }
         }
@@ -934,6 +999,8 @@
 
         void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
 
+        void onLost(@NonNull Network network);
+
         void onSubscriptionsChanged(int defaultDataSubId);
 
         void onServiceStateChanged(ServiceState serviceState);
@@ -949,4 +1016,57 @@
         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
                 @Nullable WifiEntry connectedEntry);
     }
+
+    void makeOverlayToast(int stringId) {
+        final Resources res = mContext.getResources();
+
+        final SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
+                res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
+                res.getConfiguration().orientation);
+        if (systemUIToast == null) {
+            return;
+        }
+
+        View toastView = systemUIToast.getView();
+
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        params.format = PixelFormat.TRANSLUCENT;
+        params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+        params.y = systemUIToast.getYOffset();
+
+        int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(),
+                res.getConfiguration().getLayoutDirection());
+        params.gravity = absGravity;
+        if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+            params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT;
+        }
+        if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+            params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT;
+        }
+
+        mWindowManager.addView(toastView, params);
+
+        Animator inAnimator = systemUIToast.getInAnimation();
+        if (inAnimator != null) {
+            inAnimator.start();
+        }
+
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                Animator outAnimator = systemUIToast.getOutAnimation();
+                if (outAnimator != null) {
+                    outAnimator.start();
+                    outAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animator) {
+                            mWindowManager.removeViewImmediate(toastView);
+                        }
+                    });
+                }
+            }
+        }, SHORT_DURATION_TIMEOUT);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 26781f4..2133cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -29,8 +29,8 @@
 import android.graphics.Bitmap;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
+import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
 import android.media.MediaRecorder;
@@ -187,77 +187,63 @@
      * @param refreshRate Desired refresh rate
      * @return array with supported width, height, and refresh rate
      */
-    private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
-        double maxScale = 0;
+    private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate)
+            throws IOException {
+        String videoType = MediaFormat.MIMETYPE_VIDEO_AVC;
 
-        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-        MediaCodecInfo.VideoCapabilities maxInfo = null;
-        for (MediaCodecInfo codec : codecList.getCodecInfos()) {
-            String videoType = MediaFormat.MIMETYPE_VIDEO_AVC;
-            String[] types = codec.getSupportedTypes();
-            for (String t : types) {
-                if (!t.equalsIgnoreCase(videoType)) {
-                    continue;
-                }
-                MediaCodecInfo.CodecCapabilities capabilities =
-                        codec.getCapabilitiesForType(videoType);
-                if (capabilities != null && capabilities.getVideoCapabilities() != null) {
-                    MediaCodecInfo.VideoCapabilities vc = capabilities.getVideoCapabilities();
+        // Get max size from the decoder, to ensure recordings will be playable on device
+        MediaCodec decoder = MediaCodec.createDecoderByType(videoType);
+        MediaCodecInfo.VideoCapabilities vc = decoder.getCodecInfo()
+                .getCapabilitiesForType(videoType).getVideoCapabilities();
+        decoder.release();
 
-                    int width = vc.getSupportedWidths().getUpper();
-                    int height = vc.getSupportedHeights().getUpper();
+        // Check if we can support screen size as-is
+        int width = vc.getSupportedWidths().getUpper();
+        int height = vc.getSupportedHeights().getUpper();
 
-                    int screenWidthAligned = screenWidth;
-                    if (screenWidthAligned % vc.getWidthAlignment() != 0) {
-                        screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
-                    }
-                    int screenHeightAligned = screenHeight;
-                    if (screenHeightAligned % vc.getHeightAlignment() != 0) {
-                        screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
-                    }
+        int screenWidthAligned = screenWidth;
+        if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+            screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+        }
+        int screenHeightAligned = screenHeight;
+        if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+            screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+        }
 
-                    if (width >= screenWidthAligned && height >= screenHeightAligned
-                            && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
-                        // Desired size is supported, now get the rate
-                        int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
-                                screenHeightAligned).getUpper().intValue();
+        if (width >= screenWidthAligned && height >= screenHeightAligned
+                && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
+            // Desired size is supported, now get the rate
+            int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+                    screenHeightAligned).getUpper().intValue();
 
-                        if (maxRate < refreshRate) {
-                            refreshRate = maxRate;
-                        }
-                        Log.d(TAG, "Screen size supported at rate " + refreshRate);
-                        return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
-                    }
-
-                    // Otherwise, continue searching
-                    double scale = Math.min(((double) width / screenWidth),
-                            ((double) height / screenHeight));
-                    if (scale > maxScale) {
-                        maxScale = Math.min(1, scale);
-                        maxInfo = vc;
-                    }
-                }
+            if (maxRate < refreshRate) {
+                refreshRate = maxRate;
             }
+            Log.d(TAG, "Screen size supported at rate " + refreshRate);
+            return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
         }
 
-        // Resize for max supported size
-        int scaledWidth = (int) (screenWidth * maxScale);
-        int scaledHeight = (int) (screenHeight * maxScale);
-        if (scaledWidth % maxInfo.getWidthAlignment() != 0) {
-            scaledWidth -= (scaledWidth % maxInfo.getWidthAlignment());
+        // Otherwise, resize for max supported size
+        double scale = Math.min(((double) width / screenWidth),
+                ((double) height / screenHeight));
+
+        int scaledWidth = (int) (screenWidth * scale);
+        int scaledHeight = (int) (screenHeight * scale);
+        if (scaledWidth % vc.getWidthAlignment() != 0) {
+            scaledWidth -= (scaledWidth % vc.getWidthAlignment());
         }
-        if (scaledHeight % maxInfo.getHeightAlignment() != 0) {
-            scaledHeight -= (scaledHeight % maxInfo.getHeightAlignment());
+        if (scaledHeight % vc.getHeightAlignment() != 0) {
+            scaledHeight -= (scaledHeight % vc.getHeightAlignment());
         }
 
         // Find max supported rate for size
-        int maxRate = maxInfo.getSupportedFrameRatesFor(scaledWidth, scaledHeight)
+        int maxRate = vc.getSupportedFrameRatesFor(scaledWidth, scaledHeight)
                 .getUpper().intValue();
         if (maxRate < refreshRate) {
             refreshRate = maxRate;
         }
 
-        Log.d(TAG, "Resized by " + maxScale + ": " + scaledWidth + ", " + scaledHeight
+        Log.d(TAG, "Resized by " + scale + ": " + scaledWidth + ", " + scaledHeight
                 + ", " + refreshRate);
         return new int[]{scaledWidth, scaledHeight, refreshRate};
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0bcec07..17bcfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -800,7 +800,9 @@
      * Show message on the keyguard for how the user can unlock/enter their device.
      */
     public void showActionToUnlock() {
-        if (mDozing) {
+        if (mDozing
+                && !mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+                        KeyguardUpdateMonitor.getCurrentUser())) {
             return;
         }
 
@@ -811,7 +813,7 @@
                 String message = mContext.getString(R.string.keyguard_retry);
                 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
             }
-        } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+        } else {
             showTransientIndication(mContext.getString(R.string.keyguard_unlock),
                     false /* isError */, true /* hideOnScreenOff */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 2a8771e..7aa2dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -19,7 +19,6 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
-import android.app.WallpaperManager
 import android.os.SystemClock
 import android.os.Trace
 import android.util.IndentingPrintWriter
@@ -42,6 +41,7 @@
 import com.android.systemui.statusbar.phone.PanelExpansionListener
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.WallpaperController
 import java.io.FileDescriptor
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -58,7 +58,7 @@
     private val biometricUnlockController: BiometricUnlockController,
     private val keyguardStateController: KeyguardStateController,
     private val choreographer: Choreographer,
-    private val wallpaperManager: WallpaperManager,
+    private val wallpaperController: WallpaperController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val dozeParameters: DozeParameters,
     dumpManager: DumpManager
@@ -215,15 +215,7 @@
         Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
         lastAppliedBlur = blur
-        try {
-            if (root.isAttachedToWindow && root.windowToken != null) {
-                wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
-            } else {
-                Log.i(TAG, "Won't set zoom. Window not attached $root")
-            }
-        } catch (e: IllegalArgumentException) {
-            Log.w(TAG, "Can't set zoom. Window is gone: ${root.windowToken}", e)
-        }
+        wallpaperController.setNotificationShadeZoom(zoomOut)
         listeners.forEach {
             it.onWallpaperZoomOutChanged(zoomOut)
             it.onBlurRadiusChanged(blur)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index b053b5d..d7f3225 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.settings.SecureSettings
 import java.lang.RuntimeException
@@ -67,6 +68,7 @@
     private val contentResolver: ContentResolver,
     private val configurationController: ConfigurationController,
     private val statusBarStateController: StatusBarStateController,
+    private val deviceProvisionedController: DeviceProvisionedController,
     private val execution: Execution,
     @Main private val uiExecutor: Executor,
     @Main private val handler: Handler,
@@ -95,6 +97,55 @@
         }
     }
 
+    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+        execution.assertIsMainThread()
+        val filteredTargets = targets.filter(::filterSmartspaceTarget)
+        plugin?.onTargetsAvailable(filteredTargets)
+    }
+
+    private val userTrackerCallback = object : UserTracker.Callback {
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            execution.assertIsMainThread()
+            reloadSmartspace()
+        }
+    }
+
+    private val settingsObserver = object : ContentObserver(handler) {
+        override fun onChange(selfChange: Boolean, uri: Uri?) {
+            execution.assertIsMainThread()
+            reloadSmartspace()
+        }
+    }
+
+    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+        override fun onThemeChanged() {
+            execution.assertIsMainThread()
+            updateTextColorFromWallpaper()
+        }
+    }
+
+    private val statusBarStateListener = object : StatusBarStateController.StateListener {
+        override fun onDozeAmountChanged(linear: Float, eased: Float) {
+            execution.assertIsMainThread()
+            smartspaceViews.forEach { it.setDozeAmount(eased) }
+        }
+    }
+
+    private val deviceProvisionedListener =
+        object : DeviceProvisionedController.DeviceProvisionedListener {
+            override fun onDeviceProvisionedChanged() {
+                connectSession()
+            }
+
+            override fun onUserSetupChanged() {
+                connectSession()
+            }
+        }
+
+    init {
+        deviceProvisionedController.addCallback(deviceProvisionedListener)
+    }
+
     fun isEnabled(): Boolean {
         execution.assertIsMainThread()
 
@@ -145,10 +196,20 @@
         if (plugin == null || session != null) {
             return
         }
-        val session = smartspaceManager.createSmartspaceSession(
-                SmartspaceConfig.Builder(context, "lockscreen").build())
-        session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
 
+        // Only connect after the device is fully provisioned to avoid connection caching
+        // issues
+        if (!deviceProvisionedController.isDeviceProvisioned() ||
+                !deviceProvisionedController.isCurrentUserSetup()) {
+            return
+        }
+
+        val newSession = smartspaceManager.createSmartspaceSession(
+                SmartspaceConfig.Builder(context, "lockscreen").build())
+        newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+        this.session = newSession
+
+        deviceProvisionedController.removeCallback(deviceProvisionedListener)
         userTracker.addCallback(userTrackerCallback, uiExecutor)
         contentResolver.registerContentObserver(
                 secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -159,8 +220,6 @@
         configurationController.addCallback(configChangeListener)
         statusBarStateController.addCallback(statusBarStateListener)
 
-        this.session = session
-
         reloadSmartspace()
     }
 
@@ -197,43 +256,6 @@
         plugin?.unregisterListener(listener)
     }
 
-    private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
-        execution.assertIsMainThread()
-        val filteredTargets = targets.filter(::filterSmartspaceTarget)
-        plugin?.onTargetsAvailable(filteredTargets)
-    }
-
-    private val userTrackerCallback = object : UserTracker.Callback {
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            execution.assertIsMainThread()
-            reloadSmartspace()
-        }
-
-        override fun onProfilesChanged(profiles: List<UserInfo>) {
-        }
-    }
-
-    private val settingsObserver = object : ContentObserver(handler) {
-        override fun onChange(selfChange: Boolean, uri: Uri?) {
-            execution.assertIsMainThread()
-            reloadSmartspace()
-        }
-    }
-
-    private val configChangeListener = object : ConfigurationController.ConfigurationListener {
-        override fun onThemeChanged() {
-            execution.assertIsMainThread()
-            updateTextColorFromWallpaper()
-        }
-    }
-
-    private val statusBarStateListener = object : StatusBarStateController.StateListener {
-        override fun onDozeAmountChanged(linear: Float, eased: Float) {
-            execution.assertIsMainThread()
-            smartspaceViews.forEach { it.setDozeAmount(eased) }
-        }
-    }
-
     private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
         return when (t.userHandle) {
             userTracker.userHandle -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 73c4b05..1530e523 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -874,7 +874,7 @@
             mRow.getImageResolver().purgeCache();
         }
 
-        private class RtlEnabledContext extends ContextWrapper {
+        private static class RtlEnabledContext extends ContextWrapper {
             private RtlEnabledContext(Context packageContext) {
                 super(packageContext);
             }
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 7babcba..4400909 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
@@ -610,6 +610,7 @@
         mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
         mGroupMembershipManager = groupMembershipManager;
         mGroupExpansionManager = groupExpansionManager;
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
     void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f85c749..75eb88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -173,6 +173,7 @@
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     // TODO: StatusBar should be encapsulated behind a Controller
     private final StatusBar mStatusBar;
+    private final NotificationGroupManagerLegacy mLegacyGroupManager;
     private final SectionHeaderController mSilentHeaderController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
 
@@ -663,6 +664,8 @@
                 mStatusBar.requestNotificationUpdate("onGroupsChanged");
             }
         });
+        mLegacyGroupManager = featureFlags.isNewNotifPipelineRenderingEnabled()
+                ? null : legacyGroupManager;
         mSilentHeaderController = silentHeaderController;
         mFeatureFlags = featureFlags;
         mNotifPipeline = notifPipeline;
@@ -1223,7 +1226,11 @@
             final boolean inSection =
                     NotificationStackScrollLayout.matchesSelection(row, selection);
             if (matchClearable && inSection) {
-                return true;
+                if (mLegacyGroupManager == null
+                        || !mLegacyGroupManager.isSummaryOfSuppressedGroup(
+                        row.getEntry().getSbn())) {
+                    return true;
+                }
             }
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 6e201048..2c76cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -116,7 +116,7 @@
 
     /**
      * Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the
-     * device while being requested when keyguard is occluded.
+     * device while being requested when keyguard is occluded or showing.
      */
     public static final int MODE_UNLOCK_COLLAPSING = 5;
 
@@ -425,6 +425,11 @@
                 if (!wasDeviceInteractive) {
                     mPendingShowBouncer = true;
                 } else {
+                    mShadeController.animateCollapsePanels(
+                            CommandQueue.FLAG_EXCLUDE_NONE,
+                            true /* force */,
+                            false /* delayed */,
+                            BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
                     mPendingShowBouncer = false;
                     mKeyguardViewController.notifyKeyguardAuthenticated(
                             false /* strongAuth */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d348954..e368aad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,6 +57,21 @@
     private int mUserSwitchPreferredY;
 
     /**
+     * Whether or not there is a custom clock face on keyguard.
+     */
+    private boolean mHasCustomClock;
+
+    /**
+     * Whether or not the NSSL contains any visible notifications.
+     */
+    private boolean mHasVisibleNotifs;
+
+    /**
+     * Height of notification stack: Sum of height of each notification.
+     */
+    private int mNotificationStackHeight;
+
+    /**
      * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
      * avatar.
      */
@@ -113,6 +128,25 @@
     private boolean mIsSplitShade;
 
     /**
+     * Top location of the udfps icon. This includes the worst case (highest) burn-in
+     * offset that would make the top physically highest on the screen.
+     *
+     * Set to -1 if udfps is not enrolled on the device.
+     */
+    private float mUdfpsTop;
+
+    /**
+     * Bottom y-position of the currently visible clock
+     */
+    private float mClockBottom;
+
+    /**
+     * If true, try to keep clock aligned to the top of the display. Else, assume the clock
+     * is center aligned.
+     */
+    private boolean mIsClockTopAligned;
+
+    /**
      * Refreshes the dimension values.
      */
     public void loadDimens(Resources res) {
@@ -120,7 +154,7 @@
                 R.dimen.keyguard_status_view_bottom_margin);
 
         mContainerTopPadding =
-                res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
+                res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
@@ -134,7 +168,7 @@
             int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
             float dark, float overStretchAmount, boolean bypassEnabled,
             int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset,
-            boolean isSplitShade) {
+            boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) {
         mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
                 userSwitchHeight);
         mPanelExpansion = panelExpansion;
@@ -148,6 +182,9 @@
         mQsExpansion = qsExpansion;
         mCutoutTopInset = cutoutTopInset;
         mIsSplitShade = isSplitShade;
+        mUdfpsTop = udfpsTop;
+        mClockBottom = clockBottom;
+        mIsClockTopAligned = isClockTopAligned;
     }
 
     public void run(Result result) {
@@ -202,8 +239,34 @@
         if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) {
             shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock);
         }
-        float clockYDark = clockY + burnInPreventionOffsetY() + shift;
 
+        int burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; // requested offset
+        final boolean hasUdfps = mUdfpsTop > -1;
+        if (hasUdfps && !mIsClockTopAligned) {
+            // ensure clock doesn't overlap with the udfps icon
+            if (mUdfpsTop < mClockBottom) {
+                // sometimes the clock textView extends beyond udfps, so let's just use the
+                // space above the KeyguardStatusView/clock as our burn-in offset
+                burnInPreventionOffsetY = (int) (clockY - mCutoutTopInset) / 2;
+                if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+                    burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+                }
+                shift = -burnInPreventionOffsetY;
+            } else {
+                float upperSpace = clockY - mCutoutTopInset;
+                float lowerSpace = mUdfpsTop - mClockBottom;
+                // center the burn-in offset within the upper + lower space
+                burnInPreventionOffsetY = (int) (lowerSpace + upperSpace) / 2;
+                if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+                    burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+                }
+                shift = (lowerSpace - upperSpace) / 2;
+            }
+        }
+
+        float clockYDark = clockY
+                + burnInPreventionOffsetY(burnInPreventionOffsetY)
+                + shift;
         return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
     }
 
@@ -235,9 +298,7 @@
         return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
     }
 
-    private float burnInPreventionOffsetY() {
-        int offset = mBurnInPreventionOffsetYClock;
-
+    private float burnInPreventionOffsetY(int offset) {
         return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
     }
 
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 198ad98..9018cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -57,6 +57,9 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.SensorLocationInternal;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -568,6 +571,8 @@
      */
     private float mKeyguardOnlyContentAlpha = 1.0f;
 
+    private float mUdfpsMaxYBurnInOffset;
+
     /**
      * Are we currently in gesture navigation
      */
@@ -798,13 +803,13 @@
         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
 
-        UserAvatarView userAvatarView = null;
+        FrameLayout userAvatarContainer = null;
         KeyguardUserSwitcherView keyguardUserSwitcherView = null;
 
         if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
             if (mKeyguardQsUserSwitchEnabled) {
                 ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
-                userAvatarView = (UserAvatarView) stub.inflate();
+                userAvatarContainer = (FrameLayout) stub.inflate();
             } else {
                 ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
                 keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
@@ -820,7 +825,7 @@
 
         updateViewControllers(
                 mView.findViewById(R.id.keyguard_status_view),
-                userAvatarView,
+                userAvatarContainer,
                 keyguardUserSwitcherView);
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -907,10 +912,11 @@
                 mView.getContext());
         mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
                 R.dimen.notification_side_paddings);
+        mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
     }
 
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
-            UserAvatarView userAvatarView,
+            FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
@@ -1066,7 +1072,7 @@
                 !mKeyguardQsUserSwitchEnabled
                         && mKeyguardUserSwitcherEnabled
                         && isUserSwitcherEnabled;
-        UserAvatarView userAvatarView = (UserAvatarView) reInflateStub(
+        FrameLayout userAvatarView = (FrameLayout) reInflateStub(
                 R.id.keyguard_qs_user_switch_view /* viewId */,
                 R.id.keyguard_qs_user_switch_stub /* stubId */,
                 R.layout.keyguard_qs_user_switch /* layoutId */,
@@ -1265,7 +1271,17 @@
         float darkamount =
                 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
                         ? 1.0f : mInterpolatedDarkAmount;
-        mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+
+        float udfpsAodTopLocation = -1f;
+        if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) {
+            FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
+            final SensorLocationInternal location = props.getLocation();
+            udfpsAodTopLocation = location.sensorLocationY - location.sensorRadius
+                    - mUdfpsMaxYBurnInOffset;
+        }
+
+        mClockPositionAlgorithm.setup(
+                mStatusBarHeaderHeightKeyguard,
                 expandedFraction,
                 mKeyguardStatusViewController.getLockscreenHeight(),
                 userIconHeight,
@@ -1274,7 +1290,10 @@
                 bypassEnabled, getUnlockedStackScrollerPadding(),
                 computeQsExpansionFraction(),
                 mDisplayTopInset,
-                mShouldUseSplitNotificationShade);
+                mShouldUseSplitNotificationShade,
+                udfpsAodTopLocation,
+                mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
+                mKeyguardStatusViewController.isClockTopAligned());
         mClockPositionAlgorithm.run(mClockPositionResult);
         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         boolean animateClock = animate || mAnimateNextPositionUpdate;
@@ -3546,6 +3565,7 @@
     }
 
     public void dozeTimeTick() {
+        mLockIconViewController.dozeTimeTick();
         mKeyguardBottomArea.dozeTimeTick();
         mKeyguardStatusViewController.dozeTimeTick();
         if (mInterpolatedDarkAmount > 0) {
@@ -3758,9 +3778,12 @@
     @Override
     protected TouchHandler createTouchHandler() {
         return new TouchHandler() {
+
+            private long mLastTouchDownTime = -1L;
+
             @Override
             public boolean onInterceptTouchEvent(MotionEvent event) {
-                if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
+                if (mBlockTouches || mQs.disallowPanelTouches()) {
                     return false;
                 }
                 initDownStates(event);
@@ -3789,6 +3812,19 @@
 
             @Override
             public boolean onTouch(View v, MotionEvent event) {
+                if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                    if (event.getDownTime() == mLastTouchDownTime) {
+                        // An issue can occur when swiping down after unlock, where multiple down
+                        // events are received in this handler with identical downTimes. Until the
+                        // source of the issue can be located, detect this case and ignore.
+                        // see b/193350347
+                        Log.w(TAG, "Duplicate down event detected... ignoring");
+                        return true;
+                    }
+                    mLastTouchDownTime = event.getDownTime();
+                }
+
+
                 if (mBlockTouches || (mQsFullyExpanded && mQs != null
                         && mQs.disallowPanelTouches())) {
                     return false;
@@ -3851,10 +3887,6 @@
                     mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
                 }
 
-                if (mLockIconViewController.onTouchEvent(event)) {
-                    return true;
-                }
-
                 handled |= super.onTouch(v, event);
                 return !mDozing || mPulsing || handled;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 147ebfb..03d0bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -35,6 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.LockIconViewController;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dock.DockManager;
@@ -87,6 +88,7 @@
     private final NotificationShadeDepthController mDepthController;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    private final LockIconViewController mLockIconViewController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     private GestureDetector mGestureDetector;
@@ -137,7 +139,8 @@
             NotificationPanelViewController notificationPanelViewController,
             StatusBarWindowView statusBarWindowView,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            LockIconViewController lockIconViewController) {
         mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -162,6 +165,7 @@
         mStatusBarWindowView = statusBarWindowView;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mLockIconViewController = lockIconViewController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -234,6 +238,7 @@
                 if (!isCancel && mService.shouldIgnoreTouch()) {
                     return false;
                 }
+
                 if (isDown) {
                     setTouchActive(true);
                     mTouchCancelled = false;
@@ -244,6 +249,7 @@
                 if (mTouchCancelled || mExpandAnimationRunning) {
                     return false;
                 }
+
                 mFalsingCollector.onTouchEvent(ev);
                 mGestureDetector.onTouchEvent(ev);
                 mStatusBarKeyguardViewManager.onTouch(ev);
@@ -259,9 +265,17 @@
                 if (isDown) {
                     mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev);
                 }
+
                 if (mStatusBarStateController.isDozing()) {
                     mService.mDozeScrimController.extendPulse();
                 }
+                mLockIconViewController.onTouchEvent(
+                        ev,
+                        () -> mService.wakeUpIfDozing(
+                                SystemClock.uptimeMillis(),
+                                mView,
+                                "LOCK_ICON_TOUCH"));
+
                 // In case we start outside of the view bounds (below the status bar), we need to
                 // dispatch
                 // the touch manually as the view system can't accommodate for touches outside of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index eca91a3..e775e96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -182,8 +182,7 @@
         }
         boolean fullyClosed = true;
         boolean fullyOpened = false;
-        if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
-        PanelViewController pv = mPanel;
+        if (SPEW) LOG("panelExpansionChanged: start state=%d, f=%.1f", mState, frac);
         mExpanded = expanded;
         mPanelFraction = frac;
         updateVisibility();
@@ -194,9 +193,7 @@
                 onPanelPeeked();
             }
             fullyClosed = false;
-            final float thisFrac = pv.getExpandedFraction();
-            if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
-            fullyOpened = thisFrac >= 1f;
+            fullyOpened = frac >= 1f;
         }
         if (fullyOpened && !mTracking) {
             go(STATE_OPEN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6b96f3c..ee7725f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -228,7 +228,9 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.MessageRouter;
 import com.android.systemui.volume.VolumeComponent;
@@ -537,7 +539,9 @@
     private final FeatureFlags mFeatureFlags;
     private final UnfoldTransitionConfig mUnfoldTransitionConfig;
     private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
+    private final Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
     private final Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimation;
+    private final WallpaperController mWallpaperController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final MessageRouter mMessageRouter;
     private final WallpaperManager mWallpaperManager;
@@ -771,7 +775,9 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+            Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
             Lazy<StatusBarMoveFromCenterAnimationController> statusBarUnfoldAnimationController,
+            WallpaperController wallpaperController,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
@@ -865,6 +871,8 @@
         mBrightnessSliderFactory = brightnessSliderFactory;
         mUnfoldTransitionConfig = unfoldTransitionConfig;
         mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
+        mUnfoldWallpaperController = unfoldTransitionWallpaperController;
+        mWallpaperController = wallpaperController;
         mMoveFromCenterAnimation = statusBarUnfoldAnimationController;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
@@ -1052,6 +1060,7 @@
 
         if (mUnfoldTransitionConfig.isEnabled()) {
             mUnfoldLightRevealOverlayAnimation.get().init();
+            mUnfoldWallpaperController.get().init();
         }
 
         mPluginManager.addPluginListener(
@@ -1117,6 +1126,7 @@
         inflateStatusBarWindow();
         mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
         mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
+        mWallpaperController.setRootView(mNotificationShadeWindowView);
 
         // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
@@ -4294,6 +4304,8 @@
                 return;
             }
             WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
+            mWallpaperController.onWallpaperInfoUpdated(info);
+
             final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
             // If WallpaperInfo is null, it must be ImageWallpaper.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index f8120a8..143aaba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -8,13 +8,13 @@
 import android.database.ContentObserver
 import android.os.Handler
 import android.provider.Settings
-import com.android.systemui.statusbar.StatusBarState
 import android.view.View
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.StatusBarStateControllerImpl
 import com.android.systemui.statusbar.notification.AnimatableProperty
 import com.android.systemui.statusbar.notification.PropertyAnimator
@@ -61,6 +61,7 @@
     private var shouldAnimateInKeyguard = false
     private var lightRevealAnimationPlaying = false
     private var aodUiAnimationPlaying = false
+    private var callbacks = HashSet<Callback>()
 
     /**
      * The result of our decision whether to play the screen off animation in
@@ -72,11 +73,17 @@
     private val lightRevealAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
         duration = LIGHT_REVEAL_ANIMATION_DURATION
         interpolator = Interpolators.LINEAR
-        addUpdateListener { lightRevealScrim.revealAmount = it.animatedValue as Float }
+        addUpdateListener {
+            lightRevealScrim.revealAmount = it.animatedValue as Float
+            sendUnlockedScreenOffProgressUpdate(
+                    1f - (it.animatedFraction as Float),
+                    1f - (it.animatedValue as Float))
+        }
         addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationCancel(animation: Animator?) {
                 lightRevealScrim.revealAmount = 1f
                 lightRevealAnimationPlaying = false
+                sendUnlockedScreenOffProgressUpdate(0f, 0f)
             }
 
             override fun onAnimationEnd(animation: Animator?) {
@@ -243,7 +250,21 @@
         return true
     }
 
-    /**
+    fun addCallback(callback: Callback) {
+        callbacks.add(callback)
+    }
+
+    fun removeCallback(callback: Callback) {
+        callbacks.remove(callback)
+    }
+
+    fun sendUnlockedScreenOffProgressUpdate(linear: Float, eased: Float) {
+        callbacks.forEach {
+            it.onUnlockedScreenOffProgressUpdate(linear, eased)
+        }
+    }
+
+/**
      * Whether we're doing the light reveal animation or we're done with that and animating in the
      * AOD UI.
      */
@@ -262,4 +283,8 @@
     fun isScreenOffLightRevealAnimationPlaying(): Boolean {
         return lightRevealAnimationPlaying
     }
+
+    interface Callback {
+        fun onUnlockedScreenOffProgressUpdate(linear: Float, eased: Float)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index c45068e0..13eb75a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -107,7 +107,9 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.MessageRouter;
 import com.android.systemui.volume.VolumeComponent;
@@ -216,7 +218,9 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+            Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
             Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
+            WallpaperController wallpaperController,
             OngoingCallController ongoingCallController,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
@@ -312,7 +316,9 @@
                 brightnessSliderFactory,
                 unfoldTransitionConfig,
                 unfoldLightRevealOverlayAnimation,
+                unfoldTransitionWallpaperController,
                 statusBarMoveFromCenterAnimation,
+                wallpaperController,
                 ongoingCallController,
                 animationScheduler,
                 locationPublisher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 62ba56a..eeff010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -103,7 +103,16 @@
             }
         }
 
+        // Fix for b/199600334
+        override fun onEntryCleanUp(entry: NotificationEntry) {
+            removeChipIfNeeded(entry)
+        }
+
         override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+            removeChipIfNeeded(entry)
+        }
+
+        private fun removeChipIfNeeded(entry: NotificationEntry) {
             if (entry.sbn.key == callNotificationInfo?.key) {
                 removeChip()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index a0edc7c..1e52511 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -149,7 +149,7 @@
     private void reinflate() {
         int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
         mStatusBarWindow.removeView(mBrightnessMirror);
-        mBrightnessMirror = (FrameLayout) LayoutInflater.from(mBrightnessMirror.getContext())
+        mBrightnessMirror = (FrameLayout) LayoutInflater.from(mStatusBarWindow.getContext())
                 .inflate(R.layout.brightness_mirror_container, mStatusBarWindow, false);
         mToggleSliderController = setMirrorLayout();
         mStatusBarWindow.addView(mBrightnessMirror, index);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 5e70d0d..d838a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -27,6 +27,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
 
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardVisibilityHelper;
@@ -56,7 +57,7 @@
  * Manages the user switch on the Keyguard that is used for opening the QS user panel.
  */
 @KeyguardUserSwitcherScope
-public class KeyguardQsUserSwitchController extends ViewController<UserAvatarView> {
+public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> {
 
     private static final String TAG = "KeyguardQsUserSwitchController";
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -76,6 +77,7 @@
     private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
     private final KeyguardUserDetailAdapter mUserDetailAdapter;
     private NotificationPanelViewController mNotificationPanelViewController;
+    private UserAvatarView mUserAvatarView;
     UserSwitcherController.UserRecord mCurrentUser;
 
     // State info for the user switch and keyguard
@@ -111,7 +113,7 @@
 
     @Inject
     public KeyguardQsUserSwitchController(
-            UserAvatarView view,
+            FrameLayout view,
             Context context,
             @Main Resources resources,
             ScreenLifecycle screenLifecycle,
@@ -143,6 +145,7 @@
     protected void onInit() {
         super.onInit();
         if (DEBUG) Log.d(TAG, "onInit");
+        mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar);
         mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
             @Override
             public View getView(int position, View convertView, ViewGroup parent) {
@@ -150,11 +153,10 @@
             }
         };
 
-        mView.setOnClickListener(v -> {
+        mUserAvatarView.setOnClickListener(v -> {
             if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                 return;
             }
-
             if (isListAnimating()) {
                 return;
             }
@@ -163,7 +165,7 @@
             openQsUserPanel();
         });
 
-        mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+        mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
             public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                 super.onInitializeAccessibilityNodeInfo(host, info);
                 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
@@ -237,12 +239,12 @@
                     R.string.accessibility_multi_user_switch_switcher);
         }
 
-        if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
-            mView.setContentDescription(contentDescription);
+        if (!TextUtils.equals(mUserAvatarView.getContentDescription(), contentDescription)) {
+            mUserAvatarView.setContentDescription(contentDescription);
         }
 
         int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL;
-        mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
+        mUserAvatarView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
     }
 
     Drawable getCurrentUserIcon() {
@@ -269,7 +271,7 @@
      * Get the height of the keyguard user switcher view when closed.
      */
     public int getUserIconHeight() {
-        return mView.getHeight();
+        return mUserAvatarView.getHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 3490e15..a543c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -91,8 +91,7 @@
     private int mImsType = IMS_TYPE_WWAN;
     // Save entire info for logging, we only use the id.
     final SubscriptionInfo mSubscriptionInfo;
-    // @VisibleForDemoMode
-    Map<String, MobileIconGroup> mNetworkToIconLookup;
+    private Map<String, MobileIconGroup> mNetworkToIconLookup;
 
     // Since some pieces of the phone state are interdependent we store it locally,
     // this could potentially become part of MobileState for simplification/complication
@@ -108,8 +107,6 @@
     private Config mConfig;
     @VisibleForTesting
     boolean mInflateSignalStrengths = false;
-    private MobileStatusTracker.Callback mCallback;
-    private RegistrationCallback mRegistrationCallback;
     private int mLastWwanLevel;
     private int mLastWlanLevel;
     private int mLastWlanCrossSimLevel;
@@ -121,6 +118,82 @@
     // Where to copy the next state into.
     private int mMobileStatusHistoryIndex;
 
+    private final MobileStatusTracker.Callback mMobileCallback =
+            new MobileStatusTracker.Callback() {
+                private String mLastStatus;
+
+                @Override
+                public void onMobileStatusChanged(boolean updateTelephony,
+                        MobileStatus mobileStatus) {
+                    if (Log.isLoggable(mTag, Log.DEBUG)) {
+                        Log.d(mTag, "onMobileStatusChanged="
+                                + " updateTelephony=" + updateTelephony
+                                + " mobileStatus=" + mobileStatus.toString());
+                    }
+                    String currentStatus = mobileStatus.toString();
+                    if (!currentStatus.equals(mLastStatus)) {
+                        mLastStatus = currentStatus;
+                        String status = new StringBuilder()
+                                .append(SSDF.format(System.currentTimeMillis())).append(",")
+                                .append(currentStatus)
+                                .toString();
+                        recordLastMobileStatus(status);
+                    }
+                    updateMobileStatus(mobileStatus);
+                    if (updateTelephony) {
+                        updateTelephony();
+                    } else {
+                        notifyListenersIfNecessary();
+                    }
+                }
+            };
+
+    private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
+        @Override
+        public void onRegistered(ImsRegistrationAttributes attributes) {
+            Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+            int imsTransportType = attributes.getTransportType();
+            int registrationAttributes = attributes.getAttributeFlags();
+            if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+                mImsType = IMS_TYPE_WWAN;
+                IconState statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+            } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+                if (registrationAttributes == 0) {
+                    mImsType = IMS_TYPE_WLAN;
+                    IconState statusIcon = new IconState(
+                            true,
+                            getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+                            getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                } else if (registrationAttributes
+                        == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+                    mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+                    IconState statusIcon = new IconState(
+                            true,
+                            getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+                            getCallStrengthDescription(
+                                    mLastWlanCrossSimLevel, /* isWifi= */false));
+                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                }
+            }
+        }
+
+        @Override
+        public void onUnregistered(ImsReasonInfo info) {
+            Log.d(mTag, "onDeregistered: " + "info=" + info);
+            mImsType = IMS_TYPE_WWAN;
+            IconState statusIcon = new IconState(
+                    true,
+                    getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                    getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+        }
+    };
+
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(
@@ -144,8 +217,8 @@
         mPhone = phone;
         mDefaults = defaults;
         mSubscriptionInfo = info;
-        mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator)
-                .toString();
+        mNetworkNameSeparator = getTextIfExists(
+                R.string.status_bar_network_name_separator).toString();
         mNetworkNameDefault = getTextIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default).toString();
         mReceiverHandler = new Handler(receiverLooper);
@@ -165,83 +238,9 @@
                 updateTelephony();
             }
         };
-        mCallback = new MobileStatusTracker.Callback() {
-            private String mLastStatus;
-
-            @Override
-            public void onMobileStatusChanged(boolean updateTelephony,
-                    MobileStatus mobileStatus) {
-                if (Log.isLoggable(mTag, Log.DEBUG)) {
-                    Log.d(mTag, "onMobileStatusChanged="
-                            + " updateTelephony=" + updateTelephony
-                            + " mobileStatus=" + mobileStatus.toString());
-                }
-                String currentStatus = mobileStatus.toString();
-                if (!currentStatus.equals(mLastStatus)) {
-                    mLastStatus = currentStatus;
-                    String status = new StringBuilder()
-                            .append(SSDF.format(System.currentTimeMillis())).append(",")
-                            .append(currentStatus)
-                            .toString();
-                    recordLastMobileStatus(status);
-                }
-                updateMobileStatus(mobileStatus);
-                if (updateTelephony) {
-                    updateTelephony();
-                } else {
-                    notifyListenersIfNecessary();
-                }
-            }
-        };
-
-        mRegistrationCallback = new RegistrationCallback() {
-            @Override
-            public void onRegistered(ImsRegistrationAttributes attributes) {
-                Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
-                int imsTransportType = attributes.getTransportType();
-                int registrationAttributes = attributes.getAttributeFlags();
-                if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
-                    mImsType = IMS_TYPE_WWAN;
-                    IconState statusIcon = new IconState(
-                            true,
-                            getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                            getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
-                    if (registrationAttributes == 0) {
-                        mImsType = IMS_TYPE_WLAN;
-                        IconState statusIcon = new IconState(
-                                true,
-                                getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
-                                getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
-                        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                    } else if (registrationAttributes
-                            == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
-                        mImsType = IMS_TYPE_WLAN_CROSS_SIM;
-                        IconState statusIcon = new IconState(
-                                true,
-                                getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
-                                getCallStrengthDescription(
-                                        mLastWlanCrossSimLevel, /* isWifi= */false));
-                        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-                    }
-                }
-            }
-
-            @Override
-            public void onUnregistered(ImsReasonInfo info) {
-                Log.d(mTag, "onDeregistered: " + "info=" + info);
-                mImsType = IMS_TYPE_WWAN;
-                IconState statusIcon = new IconState(
-                        true,
-                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
-                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
-                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
-            }
-        };
         mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
         mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
-                info, mDefaults, mCallback);
+                info, mDefaults, mMobileCallback);
         mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled();
         mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
     }
@@ -385,89 +384,83 @@
         if (mCurrentState.inetCondition == 0) {
             dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
         }
-        final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
-                || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
-                && mCurrentState.userSetup;
+
+        final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
+        final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
+
+        MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+                sbInfo.icon,
+                qsInfo.icon,
+                sbInfo.ratTypeIcon,
+                qsInfo.ratTypeIcon,
+                mCurrentState.hasActivityIn(),
+                mCurrentState.hasActivityOut(),
+                dataContentDescription,
+                dataContentDescriptionHtml,
+                qsInfo.description,
+                mSubscriptionInfo.getSubscriptionId(),
+                mCurrentState.roaming,
+                sbInfo.showTriangle);
+        callback.setMobileDataIndicators(mobileDataIndicators);
+    }
+
+    private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) {
+        int qsTypeIcon = 0;
+        IconState qsIcon = null;
+        CharSequence qsDescription = null;
+
+        boolean pm = mProviderModelSetting || mProviderModelBehavior;
+        if (mCurrentState.dataSim) {
+            // If using provider model behavior, only show QS icons if the state is also default
+            if (pm && !mCurrentState.isDefault) {
+                  return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+            }
+
+            if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
+                qsTypeIcon = dataTypeIcon;
+            }
+
+            boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency;
+            qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription);
+
+            if (!mCurrentState.isEmergency) {
+                qsDescription = mCurrentState.networkName;
+            }
+        }
+
+        return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+    }
+
+    private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
+        final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
+        boolean showTriangle = false;
+        int typeIcon = 0;
+        IconState statusIcon = null;
 
         if (mProviderModelBehavior) {
-            // Show icon in QS when we are connected or data is disabled.
-            boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
-
-            int qsTypeIcon = 0;
-            IconState qsIcon = null;
-            CharSequence description = null;
-            // Only send data sim callbacks to QS.
-            if (mCurrentState.dataSim && mCurrentState.isDefault) {
-                qsTypeIcon =
-                        (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
-                qsIcon = new IconState(mCurrentState.enabled
-                        && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
-                description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
-            }
-            boolean activityIn = mCurrentState.dataConnected
-                    && !mCurrentState.carrierNetworkChangeMode
-                    && mCurrentState.activityIn;
-            boolean activityOut = mCurrentState.dataConnected
-                    && !mCurrentState.carrierNetworkChangeMode
-                    && mCurrentState.activityOut;
-            showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
-            boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
-            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
-            showDataIcon |= mCurrentState.roaming;
-            IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
+            boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
+                    && (mCurrentState.dataSim && mCurrentState.isDefault);
+            typeIcon =
+                    (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+            showDataIconStatusBar |= mCurrentState.roaming;
+            statusIcon = new IconState(
+                    showDataIconStatusBar && !mCurrentState.airplaneMode,
                     getCurrentIconId(), contentDescription);
-            MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
-                    statusIcon, qsIcon, typeIcon, qsTypeIcon,
-                    activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
-                    description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming, showTriangle);
-            callback.setMobileDataIndicators(mobileDataIndicators);
+
+            showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
         } else {
-            boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
-            IconState statusIcon = new IconState(
+            statusIcon = new IconState(
                     mCurrentState.enabled && !mCurrentState.airplaneMode,
                     getCurrentIconId(), contentDescription);
 
-            int qsTypeIcon = 0;
-            IconState qsIcon = null;
-            CharSequence description = null;
-            // Only send data sim callbacks to QS.
-            if (mProviderModelSetting) {
-                if (mCurrentState.dataSim && mCurrentState.isDefault) {
-                    qsTypeIcon =
-                            (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
-                    qsIcon = new IconState(
-                            mCurrentState.enabled && !mCurrentState.isEmergency,
-                            getQsCurrentIconId(), contentDescription);
-                    description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
-                }
-            } else {
-                if (mCurrentState.dataSim) {
-                    qsTypeIcon =
-                            (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
-                    qsIcon = new IconState(
-                            mCurrentState.enabled && !mCurrentState.isEmergency,
-                            getQsCurrentIconId(), contentDescription);
-                    description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
-                }
-            }
-
-            boolean activityIn = mCurrentState.dataConnected
-                    && !mCurrentState.carrierNetworkChangeMode
-                    && mCurrentState.activityIn;
-            boolean activityOut = mCurrentState.dataConnected
-                    && !mCurrentState.carrierNetworkChangeMode
-                    && mCurrentState.activityOut;
-            showDataIcon &= mCurrentState.isDefault || dataDisabled;
-            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
-            boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
-            MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
-                    statusIcon, qsIcon, typeIcon, qsTypeIcon,
-                    activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
-                    description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming, showTriangle);
-            callback.setMobileDataIndicators(mobileDataIndicators);
+            boolean showDataIconInStatusBar =
+                    (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+            typeIcon =
+                    (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+            showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
         }
+
+        return new SbInfo(showTriangle, typeIcon, statusIcon);
     }
 
     @Override
@@ -841,12 +834,15 @@
     public void dump(PrintWriter pw) {
         super.dump(pw);
         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
+        pw.println("  mProviderModelSetting=" + mProviderModelSetting + ",");
+        pw.println("  mProviderModelBehavior=" + mProviderModelBehavior + ",");
         pw.println("  mServiceState=" + mServiceState + ",");
         pw.println("  mSignalStrength=" + mSignalStrength + ",");
         pw.println("  mTelephonyDisplayInfo=" + mTelephonyDisplayInfo + ",");
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
+        pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
         pw.println("  MobileStatusHistory");
         int size = 0;
         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -862,4 +858,31 @@
                     + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
         }
     }
+
+    /** Box for QS icon info */
+    private static final class QsInfo {
+        final int ratTypeIcon;
+        final IconState icon;
+        final CharSequence description;
+
+        QsInfo(int typeIcon, IconState iconState, CharSequence desc) {
+            ratTypeIcon = typeIcon;
+            icon = iconState;
+            description = desc;
+        }
+    }
+
+    /** Box for StatusBar icon info */
+    private static final class SbInfo {
+        final boolean showTriangle;
+        final int ratTypeIcon;
+        final IconState icon;
+
+        SbInfo(boolean show, int typeIcon, IconState iconState) {
+            showTriangle = show;
+            ratTypeIcon = typeIcon;
+            icon = iconState;
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index eeea699..9aec903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -81,7 +81,7 @@
                 .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
                 .append(",activityIn=").append(activityIn)
                 .append(",activityOut=").append(activityOut)
-                .append(",description=").append(description)
+                .append(",qsDescription=").append(description)
                 .append(",isTransient=").append(isTransient)
                 .append(",statusLabel=").append(statusLabel)
                 .append(']').toString();
@@ -100,8 +100,7 @@
         public boolean activityOut;
         public CharSequence typeContentDescription;
         public CharSequence typeContentDescriptionHtml;
-        public CharSequence description;
-        public boolean isWide;
+        public CharSequence qsDescription;
         public int subId;
         public boolean roaming;
         public boolean showTriangle;
@@ -109,7 +108,7 @@
         public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml,
-                CharSequence description, boolean isWide, int subId, boolean roaming,
+                CharSequence qsDescription, int subId, boolean roaming,
                 boolean showTriangle) {
             this.statusIcon = statusIcon;
             this.qsIcon = qsIcon;
@@ -119,8 +118,7 @@
             this.activityOut = activityOut;
             this.typeContentDescription = typeContentDescription;
             this.typeContentDescriptionHtml = typeContentDescriptionHtml;
-            this.description = description;
-            this.isWide = isWide;
+            this.qsDescription = qsDescription;
             this.subId = subId;
             this.roaming = roaming;
             this.showTriangle = showTriangle;
@@ -137,8 +135,7 @@
                 .append(",activityOut=").append(activityOut)
                 .append(",typeContentDescription=").append(typeContentDescription)
                 .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
-                .append(",description=").append(description)
-                .append(",isWide=").append(isWide)
+                .append(",description=").append(qsDescription)
                 .append(",subId=").append(subId)
                 .append(",roaming=").append(roaming)
                 .append(",showTriangle=").append(showTriangle)
@@ -186,11 +183,13 @@
         default void setCallIndicator(IconState statusIcon, int subId) {}
     }
 
-    public interface EmergencyListener {
+    /** */
+    interface EmergencyListener {
         void setEmergencyCallsOnly(boolean emergencyOnly);
     }
 
-    public static class IconState {
+    /** */
+    class IconState {
         public final boolean visible;
         public final int icon;
         public final String contentDescription;
@@ -220,7 +219,7 @@
      * Tracks changes in access points.  Allows listening for changes, scanning for new APs,
      * and connecting to new ones.
      */
-    public interface AccessPointController {
+    interface AccessPointController {
         void addAccessPointCallback(AccessPointCallback callback);
         void removeAccessPointCallback(AccessPointCallback callback);
         void scanForAccessPoints();
@@ -230,7 +229,7 @@
         boolean canConfigWifi();
         boolean canConfigMobileData();
 
-        public interface AccessPointCallback {
+        interface AccessPointCallback {
             void onAccessPointsChanged(List<WifiEntry> accessPoints);
             void onSettingsActivityTriggered(Intent settingsIntent);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index fc19564..3f7ddc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -163,7 +163,7 @@
         int qsTypeIcon = 0;
         IconState qsIcon = null;
         if (sbVisible) {
-            qsTypeIcon = icons.qsDataType;
+            qsTypeIcon = icons.dataType;
             qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(),
                     contentDescription);
         }
@@ -172,7 +172,7 @@
         MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
                 statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
-                dataContentDescriptionHtml, description, icons.isWide,
+                dataContentDescriptionHtml, description,
                 mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true
         );
         callback.setMobileDataIndicators(mobileDataIndicators);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index fe0b970..e2d0bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -178,8 +178,7 @@
             Map<String, OverlayIdentifier> categoryToPackage,
             FabricatedOverlay[] pendingCreation,
             int currentUser,
-            Set<UserHandle> managedProfiles,
-            Runnable onOverlaysApplied) {
+            Set<UserHandle> managedProfiles) {
         mBgExecutor.execute(() -> {
 
             // Disable all overlays that have not been specified in the user setting.
@@ -226,7 +225,6 @@
 
             try {
                 mOverlayManager.commit(transaction.build());
-                mMainExecutor.execute(onOverlaysApplied);
             } catch (SecurityException | IllegalStateException e) {
                 Log.e(TAG, "setEnabled failed", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index a288d999..cd5865f 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -549,21 +549,17 @@
                     .map(key -> key + " -> " + categoryToPackage.get(key)).collect(
                             Collectors.joining(", ")));
         }
-        Runnable overlaysAppliedRunnable = this::onOverlaysApplied;
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
                     mSecondaryOverlay, mNeutralOverlay
-            }, currentUser, managedProfiles, overlaysAppliedRunnable);
+            }, currentUser, managedProfiles);
         } else {
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
-                    managedProfiles, overlaysAppliedRunnable);
+                    managedProfiles);
         }
     }
 
-    protected void onOverlaysApplied() {
-    }
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("mSystemColors=" + mCurrentColors);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
new file mode 100644
index 0000000..8dd3d6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.util.WallpaperController
+import javax.inject.Inject
+
+@SysUISingleton
+class UnfoldTransitionWallpaperController @Inject constructor(
+    private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+    private val wallpaperController: WallpaperController
+) {
+
+    fun init() {
+        unfoldTransitionProgressProvider.addCallback(TransitionListener())
+    }
+
+    private inner class TransitionListener : TransitionProgressListener {
+        override fun onTransitionProgress(progress: Float) {
+            wallpaperController.setUnfoldTransitionZoom(progress)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
index a22793b..516efc0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java
@@ -109,6 +109,12 @@
         }
     }
 
+    @Override
+    public void setTintList(ColorStateList tint) {
+        super.setTintList(tint);
+        mTint = tint;
+    }
+
     private void applyTint() {
         if (getDrawable() != null && mTint != null) {
             getDrawable().mutate().setTintList(mTint);
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
index de5a363..14190fa 100644
--- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -23,8 +23,9 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
-import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 
 import javax.inject.Inject;
@@ -34,39 +35,54 @@
  */
 @SysUISingleton
 public class CarrierConfigTracker extends BroadcastReceiver {
-    private final SparseArray<Boolean> mCallStrengthConfigs = new SparseArray<>();
-    private final SparseArray<Boolean> mNoCallingConfigs = new SparseArray<>();
+    private final SparseBooleanArray mCallStrengthConfigs = new SparseBooleanArray();
+    private final SparseBooleanArray mNoCallingConfigs = new SparseBooleanArray();
+    private final SparseBooleanArray mCarrierProvisionsWifiMergedNetworks =
+            new SparseBooleanArray();
     private final CarrierConfigManager mCarrierConfigManager;
     private boolean mDefaultCallStrengthConfigLoaded;
     private boolean mDefaultCallStrengthConfig;
     private boolean mDefaultNoCallingConfigLoaded;
     private boolean mDefaultNoCallingConfig;
+    private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded;
+    private boolean mDefaultCarrierProvisionsWifiMergedNetworks;
 
     @Inject
-    public CarrierConfigTracker(Context context) {
+    public CarrierConfigTracker(Context context, BroadcastDispatcher broadcastDispatcher) {
         mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
-        context.registerReceiver(
+        broadcastDispatcher.registerReceiver(
                 this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (intent.getAction() == CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) {
-            int subId = intent.getIntExtra(
-                    CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
-                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-                return;
-            }
-            PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
-            if (b != null) {
-                boolean hideNoCallingConfig = b.getBoolean(
-                        CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL);
-                boolean displayCallStrengthIcon = b.getBoolean(
-                        CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL);
-                mCallStrengthConfigs.put(subId, displayCallStrengthIcon);
-                mNoCallingConfigs.put(subId, hideNoCallingConfig);
-            }
+        if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+            return;
+        }
+
+        final int subId = intent.getIntExtra(
+                CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return;
+        }
+
+        final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+        if (config == null) {
+            return;
+        }
+
+        synchronized (mCallStrengthConfigs) {
+            mCallStrengthConfigs.put(subId, config.getBoolean(
+                    CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL));
+        }
+        synchronized (mNoCallingConfigs) {
+            mNoCallingConfigs.put(subId, config.getBoolean(
+                    CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL));
+        }
+        synchronized (mCarrierProvisionsWifiMergedNetworks) {
+            mCarrierProvisionsWifiMergedNetworks.put(subId, config.getBoolean(
+                    CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL));
         }
     }
 
@@ -74,8 +90,10 @@
      * Returns the KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL value for the given subId.
      */
     public boolean getCallStrengthConfig(int subId) {
-        if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
-            return mCallStrengthConfigs.get(subId);
+        synchronized (mCallStrengthConfigs) {
+            if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
+                return mCallStrengthConfigs.get(subId);
+            }
         }
         if (!mDefaultCallStrengthConfigLoaded) {
             mDefaultCallStrengthConfig =
@@ -90,8 +108,10 @@
      * Returns the KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL value for the given subId.
      */
     public boolean getNoCallingConfig(int subId) {
-        if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
-            return mNoCallingConfigs.get(subId);
+        synchronized (mNoCallingConfigs) {
+            if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
+                return mNoCallingConfigs.get(subId);
+            }
         }
         if (!mDefaultNoCallingConfigLoaded) {
             mDefaultNoCallingConfig =
@@ -101,4 +121,22 @@
         }
         return mDefaultNoCallingConfig;
     }
+
+    /**
+     * Returns the KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL value for the given subId.
+     */
+    public boolean getCarrierProvisionsWifiMergedNetworksBool(int subId) {
+        synchronized (mCarrierProvisionsWifiMergedNetworks) {
+            if (mCarrierProvisionsWifiMergedNetworks.indexOfKey(subId) >= 0) {
+                return mCarrierProvisionsWifiMergedNetworks.get(subId);
+            }
+        }
+        if (!mDefaultCarrierProvisionsWifiMergedNetworksLoaded) {
+            mDefaultCarrierProvisionsWifiMergedNetworks =
+                    CarrierConfigManager.getDefaultConfig().getBoolean(
+                            CarrierConfigManager.KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL);
+            mDefaultCarrierProvisionsWifiMergedNetworksLoaded = true;
+        }
+        return mDefaultCarrierProvisionsWifiMergedNetworks;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index 3b64f9f..1059d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -69,6 +69,10 @@
         return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
     }
 
+    override fun canApplyTheme(): Boolean {
+        return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme()
+    }
+
     private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
         override fun newDrawable(): Drawable {
             return newDrawable(null, null)
@@ -82,5 +86,9 @@
         override fun getChangingConfigurations(): Int {
             return wrappedState.changingConfigurations
         }
+
+        override fun canApplyTheme(): Boolean {
+            return true
+        }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
new file mode 100644
index 0000000..db2aca8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.app.WallpaperInfo
+import android.app.WallpaperManager
+import android.util.Log
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlin.math.max
+
+private const val TAG = "WallpaperController"
+
+@SysUISingleton
+class WallpaperController @Inject constructor(private val wallpaperManager: WallpaperManager) {
+
+    var rootView: View? = null
+
+    private var notificationShadeZoomOut: Float = 0f
+    private var unfoldTransitionZoomOut: Float = 0f
+
+    private var wallpaperInfo: WallpaperInfo? = null
+
+    fun onWallpaperInfoUpdated(wallpaperInfo: WallpaperInfo?) {
+        this.wallpaperInfo = wallpaperInfo
+    }
+
+    private val shouldUseDefaultUnfoldTransition: Boolean
+        get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
+            ?: true
+
+    fun setNotificationShadeZoom(zoomOut: Float) {
+        notificationShadeZoomOut = zoomOut
+        updateZoom()
+    }
+
+    fun setUnfoldTransitionZoom(zoomOut: Float) {
+        if (shouldUseDefaultUnfoldTransition) {
+            unfoldTransitionZoomOut = zoomOut
+            updateZoom()
+        }
+    }
+
+    private fun updateZoom() {
+        setWallpaperZoom(max(notificationShadeZoomOut, unfoldTransitionZoomOut))
+    }
+
+    private fun setWallpaperZoom(zoomOut: Float) {
+        try {
+            rootView?.let { root ->
+                if (root.isAttachedToWindow && root.windowToken != null) {
+                    wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
+                } else {
+                    Log.i(TAG, "Won't set zoom. Window not attached $root")
+                }
+            }
+        } catch (e: IllegalArgumentException) {
+            Log.w(TAG, "Can't set zoom. Window is gone: ${rootView?.windowToken}", e)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 2b4b49b..9917777 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -116,7 +116,7 @@
         if (toolbar != null) {
             setActionBar(toolbar);
         }
-        setTitle("");
+        getActionBar().setDisplayShowTitleEnabled(false);
         getActionBar().setDisplayHomeAsUpEnabled(true);
         getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
         getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
@@ -220,6 +220,12 @@
     }
 
     @Override
+    protected void onStop() {
+        super.onStop();
+        finish();
+    }
+
+    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.wallet_activity_options_menu, menu);
         return super.onCreateOptionsMenu(menu);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 8d615f0..88b596c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -119,6 +119,7 @@
         when(mNotificationIcons.getLayoutParams()).thenReturn(
                 mock(RelativeLayout.LayoutParams.class));
         when(mView.getContext()).thenReturn(getContext());
+        when(mView.getResources()).thenReturn(mResources);
 
         when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
         when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
@@ -127,7 +128,6 @@
         when(mLargeClockView.getContext()).thenReturn(getContext());
 
         when(mView.isAttachedToWindow()).thenReturn(true);
-        when(mResources.getString(anyInt())).thenReturn("h:mm");
         when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
         mController = new KeyguardClockSwitchController(
                 mView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index f09d7b7..7e9f84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -19,8 +19,9 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.View.OVER_SCROLL_ALWAYS;
 import static android.view.View.OVER_SCROLL_NEVER;
+import static android.view.WindowInsets.Type.displayCutout;
 import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.systemBars;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -39,6 +40,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Insets;
+import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.testing.AndroidTestingRunner;
@@ -98,14 +100,15 @@
     private AccessibilityFloatingMenuView mMenuView;
     private RecyclerView mListView = new RecyclerView(mContext);
 
-    private int mScreenHeight;
     private int mMenuWindowHeight;
     private int mMenuHalfWidth;
     private int mMenuHalfHeight;
-    private int mScreenHalfWidth;
-    private int mScreenHalfHeight;
+    private int mDisplayHalfWidth;
+    private int mDisplayHalfHeight;
     private int mMaxWindowX;
     private int mMaxWindowY;
+    private final int mDisplayWindowWidth = 1080;
+    private final int mDisplayWindowHeight = 2340;
 
     @Before
     public void initMenuView() {
@@ -113,7 +116,10 @@
         doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
                 mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
-
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+        when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, mDisplayWindowWidth,
+                mDisplayWindowHeight));
+        when(mWindowMetrics.getWindowInsets()).thenReturn(fakeDisplayInsets());
         mMenuView = spy(
                 new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition, mListView));
     }
@@ -129,18 +135,16 @@
                 res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_width_height);
         final int menuWidth = padding * 2 + iconWidthHeight;
         final int menuHeight = (padding + iconWidthHeight) * mTargets.size() + padding;
-        final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
-        mScreenHeight = mContext.getResources().getDisplayMetrics().heightPixels;
         mMenuHalfWidth = menuWidth / 2;
         mMenuHalfHeight = menuHeight / 2;
-        mScreenHalfWidth = screenWidth / 2;
-        mScreenHalfHeight = mScreenHeight / 2;
+        mDisplayHalfWidth = mDisplayWindowWidth / 2;
+        mDisplayHalfHeight = mDisplayWindowHeight / 2;
         int marginStartEnd =
                 mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT
                         ? margin : 0;
-        mMaxWindowX = screenWidth - marginStartEnd - menuWidth;
+        mMaxWindowX = mDisplayWindowWidth - marginStartEnd - menuWidth;
         mMenuWindowHeight = menuHeight + margin * 2;
-        mMaxWindowY = mScreenHeight - mMenuWindowHeight;
+        mMaxWindowY = mDisplayWindowHeight - mMenuWindowHeight;
     }
 
     @Test
@@ -279,15 +283,15 @@
         final MotionEvent moveEvent =
                 mMotionEventHelper.obtainMotionEvent(2, 3,
                         MotionEvent.ACTION_MOVE,
-                        /* screenCenterX */mScreenHalfWidth
-                                - /* offsetXToScreenLeftHalfRegion */ 10,
-                        /* screenCenterY */ mScreenHalfHeight);
+                        /* displayCenterX */mDisplayHalfWidth
+                                - /* offsetXToDisplayLeftHalfRegion */ 10,
+                        /* displayCenterY */ mDisplayHalfHeight);
         final MotionEvent upEvent =
                 mMotionEventHelper.obtainMotionEvent(4, 5,
                         MotionEvent.ACTION_UP,
-                        /* screenCenterX */ mScreenHalfWidth
-                                - /* offsetXToScreenLeftHalfRegion */ 10,
-                        /* screenCenterY */ mScreenHalfHeight);
+                        /* displayCenterX */ mDisplayHalfWidth
+                                - /* offsetXToDisplayLeftHalfRegion */ 10,
+                        /* displayCenterY */ mDisplayHalfHeight);
 
         listView.dispatchTouchEvent(downEvent);
         listView.dispatchTouchEvent(moveEvent);
@@ -315,15 +319,15 @@
         final MotionEvent moveEvent =
                 mMotionEventHelper.obtainMotionEvent(2, 3,
                         MotionEvent.ACTION_MOVE,
-                        /* screenCenterX */mScreenHalfWidth
-                                + /* offsetXToScreenRightHalfRegion */ 10,
-                        /* screenCenterY */ mScreenHalfHeight);
+                        /* displayCenterX */mDisplayHalfWidth
+                                + /* offsetXToDisplayRightHalfRegion */ 10,
+                        /* displayCenterY */ mDisplayHalfHeight);
         final MotionEvent upEvent =
                 mMotionEventHelper.obtainMotionEvent(4, 5,
                         MotionEvent.ACTION_UP,
-                        /* screenCenterX */ mScreenHalfWidth
-                                + /* offsetXToScreenRightHalfRegion */ 10,
-                        /* screenCenterY */ mScreenHalfHeight);
+                        /* displayCenterX */ mDisplayHalfWidth
+                                + /* offsetXToDisplayRightHalfRegion */ 10,
+                        /* displayCenterY */ mDisplayHalfHeight);
 
         listView.dispatchTouchEvent(downEvent);
         listView.dispatchTouchEvent(moveEvent);
@@ -332,12 +336,12 @@
 
         assertThat((float) menuView.mCurrentLayoutParams.x).isWithin(1.0f).of(mMaxWindowX);
         assertThat((float) menuView.mCurrentLayoutParams.y).isWithin(1.0f).of(
-                /* newWindowY = screenCenterY - offsetY */ mScreenHalfHeight - mMenuHalfHeight);
+                /* newWindowY = displayCenterY - offsetY */ mDisplayHalfHeight - mMenuHalfHeight);
     }
 
 
     @Test
-    public void tapOnAndDragMenuToScreenSide_transformShapeHalfOval() {
+    public void tapOnAndDragMenuToDisplaySide_transformShapeHalfOval() {
         final Position alignRightPosition = new Position(1.0f, 0.8f);
         final RecyclerView listView = new RecyclerView(mContext);
         final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
@@ -355,13 +359,13 @@
                 mMotionEventHelper.obtainMotionEvent(2, 3,
                         MotionEvent.ACTION_MOVE,
                         /* downX */(currentWindowX + mMenuHalfWidth)
-                                + /* offsetXToScreenRightSide */ mMenuHalfWidth,
+                                + /* offsetXToDisplayRightSide */ mMenuHalfWidth,
                         /* downY */ (currentWindowY +  mMenuHalfHeight));
         final MotionEvent upEvent =
                 mMotionEventHelper.obtainMotionEvent(4, 5,
                         MotionEvent.ACTION_UP,
                         /* downX */(currentWindowX + mMenuHalfWidth)
-                                + /* offsetXToScreenRightSide */ mMenuHalfWidth,
+                                + /* offsetXToDisplayRightSide */ mMenuHalfWidth,
                         /* downY */ (currentWindowY +  mMenuHalfHeight));
 
         listView.dispatchTouchEvent(downEvent);
@@ -423,7 +427,7 @@
     }
 
     @Test
-    public void showMenuAndIme_withHigherIme_alignScreenTopEdge() {
+    public void showMenuAndIme_withHigherIme_alignDisplayTopEdge() {
         final int offset = 99999;
 
         setupBasicMenuView(mMenuView);
@@ -475,10 +479,21 @@
     private WindowInsets fakeImeInsetWith(AccessibilityFloatingMenuView menuView, int offset) {
         // Ensure the keyboard has overlapped on the menu view.
         final int fakeImeHeight =
-                mScreenHeight - (menuView.mCurrentLayoutParams.y + mMenuWindowHeight) + offset;
+                mDisplayWindowHeight - (menuView.mCurrentLayoutParams.y + mMenuWindowHeight)
+                        + offset;
         return new WindowInsets.Builder()
-                .setVisible(ime() | navigationBars(), true)
-                .setInsets(ime() | navigationBars(), Insets.of(0, 0, 0, fakeImeHeight))
+                .setVisible(ime(), true)
+                .setInsets(ime(), Insets.of(0, 0, 0, fakeImeHeight))
+                .build();
+    }
+
+    private WindowInsets fakeDisplayInsets() {
+        final int fakeStatusBarHeight = 75;
+        final int fakeNavigationBarHeight = 125;
+        return new WindowInsets.Builder()
+                .setVisible(systemBars() | displayCutout(), true)
+                .setInsets(systemBars() | displayCutout(),
+                        Insets.of(0, fakeStatusBarHeight, 0, fakeNavigationBarHeight))
                 .build();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
index eb1f15b..3553a0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
@@ -23,12 +23,16 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.test.filters.SmallTest;
@@ -52,6 +56,9 @@
     @Mock
     private WindowManager mWindowManager;
 
+    @Mock
+    private WindowMetrics mWindowMetrics;
+
     private AccessibilityFloatingMenuView mMenuView;
     private BaseTooltipView mToolTipView;
 
@@ -66,6 +73,9 @@
         doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
                 mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+        when(mWindowMetrics.getBounds()).thenReturn(new Rect());
+        when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
 
         mMenuView = new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition);
         mToolTipView = new BaseTooltipView(mContext, mMenuView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
index ca4e3e9..9eba49d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
@@ -21,12 +21,16 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 
 import androidx.test.filters.SmallTest;
 
@@ -49,6 +53,9 @@
     @Mock
     private WindowManager mWindowManager;
 
+    @Mock
+    private WindowMetrics mWindowMetrics;
+
     private AccessibilityFloatingMenuView mMenuView;
     private DockTooltipView mDockTooltipView;
     private final Position mPlaceholderPosition = new Position(0.0f, 0.0f);
@@ -62,6 +69,9 @@
         doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
                 mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+        when(mWindowMetrics.getBounds()).thenReturn(new Rect());
+        when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
 
         mMenuView = spy(new AccessibilityFloatingMenuView(mContext, mPlaceholderPosition));
         mDockTooltipView = new DockTooltipView(mContext, mMenuView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java
index dae4364..ea104a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/ItemDelegateCompatTest.java
@@ -22,12 +22,15 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -57,6 +60,8 @@
     @Mock
     private WindowManager mWindowManager;
 
+    @Mock
+    private WindowMetrics mWindowMetrics;
     private RecyclerView mListView;
     private AccessibilityFloatingMenuView mMenuView;
     private ItemDelegateCompat mItemDelegateCompat;
@@ -69,6 +74,9 @@
         doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
                 mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+        when(mWindowMetrics.getBounds()).thenReturn(new Rect());
+        when(mWindowMetrics.getWindowInsets()).thenReturn(new WindowInsets.Builder().build());
 
         mListView = new RecyclerView(mContext);
         mMenuView =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index e3566e7..deabda3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -58,7 +58,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -66,6 +65,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.Execution;
@@ -124,8 +124,6 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
-    private KeyguardViewMediator mKeyguardViewMediator;
-    @Mock
     private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
     @Mock
     private FalsingManager mFalsingManager;
@@ -153,6 +151,8 @@
     private ConfigurationController mConfigurationController;
     @Mock
     private SystemClock mSystemClock;
+    @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private FakeExecutor mFgExecutor;
 
@@ -192,6 +192,7 @@
         when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
                 .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
         when(mEnrollView.getContext()).thenReturn(mContext);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
         final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
 
         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -222,7 +223,6 @@
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
                 mKeyguardUpdateMonitor,
-                mKeyguardViewMediator,
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
@@ -236,7 +236,8 @@
                 mDisplayManager,
                 mHandler,
                 mConfigurationController,
-                mSystemClock);
+                mSystemClock,
+                mUnlockedScreenOffAnimationController);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -478,11 +479,12 @@
 
     @Test
     public void aodInterrupt() throws RemoteException {
-        // GIVEN that the overlay is showing and screen is on
+        // GIVEN that the overlay is showing and screen is on and fp is running
         mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                 BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         // WHEN fingerprint is requested because of AOD interrupt
         mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
         // THEN illumination begins
@@ -500,6 +502,7 @@
                 BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
         when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
         // WHEN it is cancelled
@@ -515,6 +518,7 @@
                 BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
         mScreenObserver.onScreenTurnedOn();
         mFgExecutor.runAllReady();
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
         when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
         // WHEN it times out
@@ -533,6 +537,24 @@
         mFgExecutor.runAllReady();
 
         // WHEN aod interrupt is received
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+        mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
+
+        // THEN no illumination because screen is off
+        verify(mUdfpsView, never()).startIllumination(any());
+    }
+
+    @Test
+    public void aodInterrupt_fingerprintNotRunning() throws RemoteException {
+        // GIVEN showing overlay
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+                mUdfpsOverlayControllerCallback);
+        mScreenObserver.onScreenTurnedOn();
+        mFgExecutor.runAllReady();
+
+        // WHEN aod interrupt is received when the fingerprint service isn't running
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
         mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
 
         // THEN no illumination because screen is off
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index be3e535..2821f3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -89,6 +90,8 @@
     @Mock
     private ConfigurationController mConfigurationController;
     @Mock
+    private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+    @Mock
     private UdfpsController mUdfpsController;
     private FakeSystemClock mSystemClock = new FakeSystemClock();
 
@@ -121,13 +124,12 @@
                 Optional.of(mStatusBar),
                 mStatusBarKeyguardViewManager,
                 mKeyguardUpdateMonitor,
-                mExecutor,
                 mDumpManager,
-                mKeyguardViewMediator,
                 mLockscreenShadeTransitionController,
                 mConfigurationController,
                 mSystemClock,
                 mKeyguardStateController,
+                mUnlockedScreenOffAnimationController,
                 mUdfpsController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 10997fa..9577c7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.FakeThreadFactory;
 import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -83,6 +84,8 @@
     private AuthController mAuthController;
     @Mock
     private UiEventLogger mUiEventLogger;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
 
     private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
@@ -114,7 +117,7 @@
         mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters,
                 asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
                 mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
-                mAuthController, mExecutor, mUiEventLogger);
+                mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController);
         mTriggers.setDozeMachine(mMachine);
         waitForSensorManager();
     }
@@ -217,6 +220,32 @@
     }
 
     @Test
+    public void testPickupGesture() {
+        // GIVEN device is in doze (screen blank, but running doze sensors)
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        // WHEN the pick up gesture is triggered and keyguard isn't occluded
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+        mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+        // THEN wakeup
+        verify(mMachine).wakeUp();
+    }
+
+    @Test
+    public void testPickupGestureDroppedKeyguardOccluded() {
+        // GIVEN device is in doze (screen blank, but running doze sensors)
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+        // WHEN the pick up gesture is triggered and keyguard IS occluded
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+        // THEN never wakeup
+        verify(mMachine, never()).wakeUp();
+    }
+
+    @Test
     public void testOnSensor_Fingerprint() {
         // GIVEN dozing state
         when(mMachine.getState()).thenReturn(DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 3e9fbcf..c464cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -18,6 +18,7 @@
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -31,6 +32,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.DisplayMetrics;
+import android.util.Pair;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
@@ -50,6 +52,8 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import com.airbnb.lottie.LottieAnimationView;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -81,6 +85,7 @@
     private @Mock DelayableExecutor mDelayableExecutor;
     private @Mock Vibrator mVibrator;
     private @Mock AuthRippleController mAuthRippleController;
+    private @Mock LottieAnimationView mAodFp;
 
     private LockIconViewController mLockIconViewController;
 
@@ -102,6 +107,7 @@
         when(mLockIconView.getContext()).thenReturn(mContext);
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+        when(mLockIconView.findViewById(anyInt())).thenReturn(mAodFp);
 
         mLockIconViewController = new LockIconViewController(
                 mLockIconView,
@@ -122,30 +128,17 @@
 
     @Test
     public void testUpdateFingerprintLocationOnInit() {
-        // GIVEN fp sensor location is available pre-init
-        final PointF udfpsLocation = new PointF(50, 75);
-        final int radius = 33;
-        final FingerprintSensorPropertiesInternal fpProps =
-                new FingerprintSensorPropertiesInternal(
-                        /* sensorId */ 0,
-                        /* strength */ 0,
-                        /* max enrollments per user */ 5,
-                        /* component info */ new ArrayList<>(),
-                        /* sensorType */ 3,
-                        /* resetLockoutRequiresHwToken */ false,
-                        List.of(new SensorLocationInternal("" /* displayId */,
-                                (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
-        when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
-        when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
+        // GIVEN fp sensor location is available pre-attached
+        Pair<Integer, PointF> udfps = setupUdfps();
 
         // WHEN lock icon view controller is initialized and attached
         mLockIconViewController.init();
         captureAttachListener();
-        mAttachListener.onViewAttachedToWindow(null);
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
 
         // THEN lock icon view location is updated with the same coordinates as fpProps
-        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
-        assertEquals(udfpsLocation, mPointCaptor.getValue());
+        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
+        assertEquals(udfps.second, mPointCaptor.getValue());
     }
 
     @Test
@@ -154,9 +147,52 @@
         when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
         when(mAuthController.getUdfpsProps()).thenReturn(null);
         mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
 
-        // GIVEN fp sensor location is available post-init
+        // GIVEN fp sensor location is available post-atttached
         captureAuthControllerCallback();
+        Pair<Integer, PointF> udfps = setupUdfps();
+
+        // WHEN all authenticators are registered
+        mAuthControllerCallback.onAllAuthenticatorsRegistered();
+
+        // THEN lock icon view location is updated with the same coordinates as fpProps
+        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(udfps.first));
+        assertEquals(udfps.second, mPointCaptor.getValue());
+    }
+
+    @Test
+    public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+        // GIVEN Udpfs sensor location is available
+        setupUdfps();
+
+        mLockIconViewController.init();
+        captureAttachListener();
+
+        // WHEN the view is attached
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+        // THEN the lock icon view background should be enabled
+        verify(mLockIconView).setUseBackground(true);
+    }
+
+    @Test
+    public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
+        // GIVEN Udfps sensor location is not available
+        when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+
+        mLockIconViewController.init();
+        captureAttachListener();
+
+        // WHEN the view is attached
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+
+        // THEN the lock icon view background should be disabled
+        verify(mLockIconView).setUseBackground(false);
+    }
+
+    private Pair<Integer, PointF> setupUdfps() {
         final PointF udfpsLocation = new PointF(50, 75);
         final int radius = 33;
         final FingerprintSensorPropertiesInternal fpProps =
@@ -172,12 +208,7 @@
         when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
         when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
 
-        // WHEN all authenticators are registered
-        mAuthControllerCallback.onAllAuthenticatorsRegistered();
-
-        // THEN lock icon view location is updated with the same coordinates as fpProps
-        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
-        assertEquals(udfpsLocation, mPointCaptor.getValue());
+        return new Pair(radius, udfpsLocation);
     }
 
     private void captureAuthControllerCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index d879186..28aed20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -86,7 +86,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        mediaDataFilter = MediaDataFilter(broadcastDispatcher, mediaResumeListener,
+        mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener,
                 lockscreenUserManager, executor, clock)
         mediaDataFilter.mediaDataManager = mediaDataManager
         mediaDataFilter.addListener(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 5c3108c..47c5545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -4,6 +4,7 @@
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
 import android.app.smartspace.SmartspaceTarget
+import android.content.Intent
 import android.graphics.Bitmap
 import android.media.MediaDescription
 import android.media.MediaMetadata
@@ -51,6 +52,7 @@
 private const val SESSION_ARTIST = "artist"
 private const val SESSION_TITLE = "title"
 private const val USER_ID = 0
+private val DISMISS_INTENT = Intent().apply { action = "dismiss" }
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -83,6 +85,7 @@
     lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
     @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
     @Mock private lateinit var mediaRecommendationItem: SmartspaceAction
+    @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
     lateinit var mediaDataManager: MediaDataManager
     lateinit var mediaNotification: StatusBarNotification
     @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@@ -146,8 +149,12 @@
         // treat mediaSessionBasedFilter as a listener for testing.
         listener = mediaSessionBasedFilter
 
-        val recommendationExtras = Bundle()
-        recommendationExtras.putString("package_name", PACKAGE_NAME)
+        val recommendationExtras = Bundle().apply {
+            putString("package_name", PACKAGE_NAME)
+            putParcelable("dismiss_intent", DISMISS_INTENT)
+        }
+        whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
+        whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
         whenever(mediaRecommendationItem.extras).thenReturn(recommendationExtras)
         whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
         whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
@@ -380,7 +387,8 @@
         verify(listener).onSmartspaceMediaDataLoaded(
             eq(KEY_MEDIA_SMARTSPACE),
             eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */,
-            PACKAGE_NAME, null, listOf(mediaRecommendationItem), 0)),
+                PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem),
+                DISMISS_INTENT, 0)),
             eq(false))
     }
 
@@ -391,7 +399,8 @@
         verify(listener).onSmartspaceMediaDataLoaded(
             eq(KEY_MEDIA_SMARTSPACE),
             eq(EMPTY_SMARTSPACE_MEDIA_DATA
-                .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, isValid = false)),
+                .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
+                    isValid = false, dismissIntent = DISMISS_INTENT)),
             eq(false))
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index ed3c473..e37f422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -56,6 +56,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityManager;
@@ -122,6 +123,8 @@
     EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
     @Mock
     EdgeBackGestureHandler mEdgeBackGestureHandler;
+    @Mock
+    NavigationBarA11yHelper mNavigationBarA11yHelper;
 
     @Rule
     public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -165,9 +168,12 @@
         Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
         when(windowManager.getDefaultDisplay()).thenReturn(
                 defaultDisplay);
-        WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
+        WindowMetrics maximumWindowMetrics = mContext.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics();
-        when(windowManager.getMaximumWindowMetrics()).thenReturn(metrics);
+        when(windowManager.getMaximumWindowMetrics()).thenReturn(maximumWindowMetrics);
+        WindowMetrics currentWindowMetrics = mContext.getSystemService(WindowManager.class)
+                .getCurrentWindowMetrics();
+        when(windowManager.getCurrentWindowMetrics()).thenReturn(currentWindowMetrics);
         doNothing().when(windowManager).addView(any(), any());
         mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
         mSysuiTestableContextExternal.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
@@ -256,6 +262,20 @@
         assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
     }
 
+    @Test
+    public void testA11yEventAfterDetach() {
+        View v = mNavigationBar.createView(null);
+        mNavigationBar.onViewAttachedToWindow(v);
+        verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
+                NavigationBarA11yHelper.NavA11yEventListener.class));
+        mNavigationBar.onViewDetachedFromWindow(v);
+        verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
+                NavigationBarA11yHelper.NavA11yEventListener.class));
+
+        // Should be safe even though the internal view is now null.
+        mNavigationBar.updateAccessibilityServicesState();
+    }
+
     private NavigationBar createNavBar(Context context) {
         DeviceProvisionedController deviceProvisionedController =
                 mock(DeviceProvisionedController.class);
@@ -287,7 +307,7 @@
                 mHandler,
                 mock(NavigationBarOverlayController.class),
                 mUiEventLogger,
-                mock(NavigationBarA11yHelper.class),
+                mNavigationBarA11yHelper,
                 mock(UserTracker.class)));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 6ff5aa0..2e1fb07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -95,11 +96,11 @@
     Resources mResources;
     @Mock
     Configuration mConfiguration;
+    @Mock
+    Runnable mHorizontalLayoutListener;
 
     private QSPanelControllerBase<QSPanel> mController;
 
-
-
     /** Implementation needed to ensure we have a reflectively-available class name. */
     private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
         protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
@@ -239,17 +240,43 @@
         when(mMediaHost.getVisible()).thenReturn(true);
 
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+        when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+        mController.init();
 
         assertThat(mController.shouldUseHorizontalLayout()).isTrue();
 
         when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+        when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
                 mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
+        mController.init();
 
         assertThat(mController.shouldUseHorizontalLayout()).isFalse();
     }
+
+    @Test
+    public void testChangeConfiguration_shouldUseHorizontalLayout() {
+        when(mMediaHost.getVisible()).thenReturn(true);
+        mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
+
+        // When device is rotated to landscape
+        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+        // Then the layout changes
+        assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+        verify(mHorizontalLayoutListener).run(); // not invoked
+
+        // When it is rotated back to portrait
+        mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
+        mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+        // Then the layout changes back
+        assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+        verify(mHorizontalLayoutListener, times(2)).run();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index a21f488..06a4ae0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -98,6 +100,10 @@
     @Mock
     CommandQueue mCommandQueue;
     FalsingManagerFake mFalsingManager = new FalsingManagerFake();
+    @Mock
+    Resources mResources;
+    @Mock
+    Configuration mConfiguration;
 
     private QSPanelController mController;
 
@@ -109,7 +115,8 @@
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
         when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
-        when(mQSPanel.getResources()).thenReturn(mContext.getResources());
+        when(mQSPanel.getResources()).thenReturn(mResources);
+        when(mResources.getConfiguration()).thenReturn(mConfiguration);
         when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
         when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
         when(mToggleSliderViewControllerFactory.create(any(), any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 126b332..a1b7210 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -232,7 +232,7 @@
         MobileDataIndicators indicators = new MobileDataIndicators(
                 mock(NetworkController.IconState.class),
                 mock(NetworkController.IconState.class),
-                0, 0, true, true, "", "", "", true, 0, true, true);
+                0, 0, true, true, "", "", "", 0, true, true);
         mSignalCallback.setMobileDataIndicators(indicators);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index 5e2d8fd..b4a66297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -3,6 +3,7 @@
 import android.app.AlarmManager
 import android.app.PendingIntent
 import android.os.Handler
+import android.provider.AlarmClock
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
@@ -98,6 +99,11 @@
     }
 
     @Test
+    fun testDefaultIntentShowAlarms() {
+        assertThat(tile.defaultIntent.action).isEqualTo(AlarmClock.ACTION_SHOW_ALARMS)
+    }
+
+    @Test
     fun testInactiveByDefault() {
         assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index baddacc..f9d5be6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,11 +1,15 @@
 package com.android.systemui.qs.tiles.dialog;
 
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -14,8 +18,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.animation.Animator;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
@@ -25,7 +31,11 @@
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.testing.TestableResources;
 import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
@@ -41,14 +51,19 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.toast.SystemUIToast;
+import com.android.systemui.toast.ToastFactory;
+import com.android.systemui.util.CarrierConfigTracker;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -65,6 +80,11 @@
     private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
     private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
 
+    //SystemUIToast
+    private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
+    private static final int TOAST_MESSAGE_STRING_ID = 1;
+    private static final String TOAST_MESSAGE_STRING = "toast message";
+
     @Mock
     private WifiManager mWifiManager;
     @Mock
@@ -74,6 +94,8 @@
     @Mock
     private Handler mHandler;
     @Mock
+    private Handler mWorkerHandler;
+    @Mock
     private ActivityStarter mActivityStarter;
     @Mock
     private GlobalSettings mGlobalSettings;
@@ -92,6 +114,8 @@
     @Mock
     private WifiEntry mWifiEntry4;
     @Mock
+    private MergedCarrierEntry mMergedCarrierEntry;
+    @Mock
     private ServiceState mServiceState;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
@@ -99,7 +123,20 @@
     private WifiUtils.InternetIconInjector mWifiIconInjector;
     @Mock
     InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+    @Mock
+    private WindowManager mWindowManager;
+    @Mock
+    private ToastFactory mToastFactory;
+    @Mock
+    private SystemUIToast mSystemUIToast;
+    @Mock
+    private View mToastView;
+    @Mock
+    private Animator mAnimator;
+    @Mock
+    private CarrierConfigTracker mCarrierConfigTracker;
 
+    private TestableResources mTestableResources;
     private MockInternetDialogController mInternetDialogController;
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private List<WifiEntry> mAccessPoints = new ArrayList<>();
@@ -108,6 +145,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mTestableResources = mContext.getOrCreateTestableResources();
         doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         when(mConnectedEntry.isDefaultNetwork()).thenReturn(true);
@@ -118,13 +156,21 @@
         when(mWifiEntry4.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
+        when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
+        when(mAccessPointController.getMergedCarrierEntry()).thenReturn(mMergedCarrierEntry);
+        when(mToastFactory.createToast(any(), anyString(), anyString(), anyInt(), anyInt()))
+            .thenReturn(mSystemUIToast);
+        when(mSystemUIToast.getView()).thenReturn(mToastView);
+        when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
+        when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
 
         mInternetDialogController = new MockInternetDialogController(mContext,
                 mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
                 mSubscriptionManager, mTelephonyManager, mWifiManager,
                 mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
-                mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController);
+                mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
+                mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker);
         mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
                 mInternetDialogController.mOnSubscriptionsChangedListener);
         mInternetDialogController.onStart(mInternetDialogCallback, true);
@@ -134,6 +180,44 @@
     }
 
     @Test
+    public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
+        when(mMergedCarrierEntry.canConnect()).thenReturn(true);
+        mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
+            TOAST_MESSAGE_STRING);
+
+        mInternetDialogController.connectCarrierNetwork();
+
+        verify(mMergedCarrierEntry).connect(null /* callback */, false /* showToast */);
+        verify(mToastFactory).createToast(any(), eq(TOAST_MESSAGE_STRING), anyString(), anyInt(),
+            anyInt());
+    }
+
+    @Test
+    public void makeOverlayToast_withGravityFlags_addViewWithLayoutParams() {
+        mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
+
+        mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+
+        ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = ArgumentCaptor.forClass(
+            WindowManager.LayoutParams.class);
+        verify(mWindowManager).addView(eq(mToastView), paramsCaptor.capture());
+        WindowManager.LayoutParams params = paramsCaptor.getValue();
+        assertThat(params.format).isEqualTo(PixelFormat.TRANSLUCENT);
+        assertThat(params.type).isEqualTo(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+        assertThat(params.horizontalWeight).isEqualTo(TOAST_PARAMS_HORIZONTAL_WEIGHT);
+        assertThat(params.verticalWeight).isEqualTo(TOAST_PARAMS_VERTICAL_WEIGHT);
+    }
+
+    @Test
+    public void makeOverlayToast_withAnimation_verifyAnimatorStart() {
+        mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
+
+        mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+
+        verify(mAnimator).start();
+    }
+
+    @Test
     public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
         mInternetDialogController.setAirplaneModeEnabled(true);
 
@@ -444,6 +528,80 @@
         verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
     }
 
+    @Test
+    public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mWifiEntry1);
+        mAccessPoints.add(mWifiEntry2);
+        mAccessPoints.add(mWifiEntry3);
+        mAccessPoints.add(mWifiEntry4);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        mWifiEntries.add(mWifiEntry2);
+        mWifiEntries.add(mWifiEntry3);
+        mWifiEntries.add(mWifiEntry4);
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+        // If the Ethernet exists, then Wi-Fi entries will cut last one.
+        reset(mInternetDialogCallback);
+        mInternetDialogController.mHasEthernet = true;
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.remove(mWifiEntry4);
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(false);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.remove(mWifiEntry3);
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+    }
+
+    @Test
+    public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
+        when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+                .thenReturn(true);
+
+        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+
+        verify(mMergedCarrierEntry, never()).setEnabled(anyBoolean());
+    }
+
+    @Test
+    public void setMergedCarrierWifiEnabledIfNeed_mergedCarrierEntryEmpty_doesntCrash() {
+        when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+                .thenReturn(false);
+        when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
+
+        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+    }
+
+    @Test
+    public void setMergedCarrierWifiEnabledIfNeed_neededSetMergedCarrierEntry_setTogether() {
+        when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
+                .thenReturn(false);
+
+        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+
+        verify(mMergedCarrierEntry).setEnabled(true);
+
+        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false);
+
+        verify(mMergedCarrierEntry).setEnabled(false);
+    }
+
     private String getResourcesString(String name) {
         return mContext.getResources().getString(getResourcesId(name));
     }
@@ -465,11 +623,14 @@
                 @Main Handler handler, @Main Executor mainExecutor,
                 BroadcastDispatcher broadcastDispatcher,
                 KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
-                KeyguardStateController keyguardStateController) {
+                KeyguardStateController keyguardStateController, WindowManager windowManager,
+                ToastFactory toastFactory, Handler workerHandler,
+                CarrierConfigTracker carrierConfigTracker) {
             super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
                     telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
                     broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
-                    keyguardStateController);
+                    keyguardStateController, windowManager, toastFactory, workerHandler,
+                    carrierConfigTracker);
             mGlobalSettings = globalSettings;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index fa9c053..c42b64a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -67,6 +67,7 @@
     private InternetDialog mInternetDialog;
     private View mDialogView;
     private View mSubTitle;
+    private LinearLayout mEthernet;
     private LinearLayout mMobileDataToggle;
     private LinearLayout mWifiToggle;
     private LinearLayout mConnectedWifi;
@@ -97,6 +98,7 @@
 
         mDialogView = mInternetDialog.mDialogView;
         mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+        mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
         mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
         mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
         mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
@@ -139,6 +141,46 @@
     }
 
     @Test
+    public void updateDialog_apmOffAndHasEthernet_showEthernet() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_apmOnAndHasEthernet_showEthernet() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
     public void updateDialog_withApmOn_mobileDataLayoutGone() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 465370b..e5ae65f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar
 
-import android.app.WallpaperManager
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
@@ -32,6 +31,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.WallpaperController
 import com.android.systemui.util.mockito.eq
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -47,10 +47,8 @@
 import org.mockito.Mockito.anyFloat
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.doThrow
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 import java.util.function.Consumer
@@ -65,7 +63,7 @@
     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var choreographer: Choreographer
-    @Mock private lateinit var wallpaperManager: WallpaperManager
+    @Mock private lateinit var wallpaperController: WallpaperController
     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var root: View
@@ -101,7 +99,7 @@
 
         notificationShadeDepthController = NotificationShadeDepthController(
                 statusBarStateController, blurUtils, biometricUnlockController,
-                keyguardStateController, choreographer, wallpaperManager,
+                keyguardStateController, choreographer, wallpaperController,
                 notificationShadeWindowController, dozeParameters, dumpManager)
         notificationShadeDepthController.shadeAnimation = shadeAnimation
         notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -209,7 +207,7 @@
         notificationShadeDepthController.qsPanelExpansion = 0.25f
         notificationShadeDepthController.onPanelExpansionChanged(1f, tracking = false)
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(wallpaperManager).setWallpaperZoomOut(any(),
+        verify(wallpaperController).setNotificationShadeZoom(
                 eq(Interpolators.getNotificationScrimAlpha(0.25f, false /* notifications */)))
     }
 
@@ -242,14 +240,14 @@
         notificationShadeDepthController.transitionToFullShadeProgress = 1f
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(0), eq(false))
-        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
+        verify(wallpaperController).setNotificationShadeZoom(eq(1f))
     }
 
     @Test
     fun updateBlurCallback_setsBlurAndZoom() {
         notificationShadeDepthController.addListener(listener)
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
+        verify(wallpaperController).setNotificationShadeZoom(anyFloat())
         verify(listener).onWallpaperZoomOutChanged(anyFloat())
         verify(blurUtils).applyBlur(any(), anyInt(), eq(false))
     }
@@ -279,21 +277,6 @@
     }
 
     @Test
-    fun updateBlurCallback_invalidWindow() {
-        `when`(root.isAttachedToWindow).thenReturn(false)
-        notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(wallpaperManager, times(0)).setWallpaperZoomOut(any(), anyFloat())
-    }
-
-    @Test
-    fun updateBlurCallback_exception() {
-        doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
-                .setWallpaperZoomOut(any(), anyFloat())
-        notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
-    }
-
-    @Test
     fun ignoreShadeBlurUntilHidden_schedulesFrame() {
         notificationShadeDepthController.blursDisabledForAppLaunch = true
         verify(choreographer).postFrameCallback(
@@ -322,7 +305,7 @@
 
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
-        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
+        verify(wallpaperController).setNotificationShadeZoom(eq(1f))
         verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 575e01cf..c8fec02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener
 import com.android.systemui.util.concurrency.FakeExecution
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -89,6 +91,8 @@
     @Mock
     private lateinit var statusBarStateController: StatusBarStateController
     @Mock
+    private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock
     private lateinit var handler: Handler
 
     @Mock
@@ -106,12 +110,15 @@
     private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
     @Captor
     private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+    @Captor
+    private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener>
 
     private lateinit var sessionListener: OnTargetsAvailableListener
     private lateinit var userListener: UserTracker.Callback
     private lateinit var settingsObserver: ContentObserver
     private lateinit var configChangeListener: ConfigurationListener
     private lateinit var statusBarStateListener: StateListener
+    private lateinit var deviceProvisionedListener: DeviceProvisionedListener
 
     private lateinit var smartspaceView: SmartspaceView
 
@@ -145,6 +152,8 @@
         `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
         `when`(userTracker.userProfiles).thenReturn(userList)
         `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
 
         setActiveUser(userHandlePrimary)
         setAllowPrivateNotifications(userHandlePrimary, true)
@@ -162,11 +171,15 @@
                 contentResolver,
                 configurationController,
                 statusBarStateController,
+                deviceProvisionedController,
                 execution,
                 executor,
                 handler,
                 Optional.of(plugin)
                 )
+
+        verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
+        deviceProvisionedListener = deviceProvisionedCaptor.value
     }
 
     @Test(expected = RuntimeException::class)
@@ -181,6 +194,27 @@
     }
 
     @Test
+    fun connectOnlyAfterDeviceIsProvisioned() {
+        // GIVEN an unprovisioned device and an attempt to connect
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
+
+        // WHEN a connection attempt is made
+        controller.buildAndConnectView(fakeParent)
+
+        // THEN no session is created
+        verify(smartspaceManager, never()).createSmartspaceSession(any())
+
+        // WHEN it does become provisioned
+        `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+        `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+        deviceProvisionedListener.onUserSetupChanged()
+
+        // THEN the session is created
+        verify(smartspaceManager).createSmartspaceSession(any())
+    }
+
+    @Test
     fun testListenersAreRegistered() {
         // GIVEN a listener is added after a session is created
         connectSession()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 60f0b68..4276f7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -274,6 +274,26 @@
     }
 
     @Test
+    public void onBiometricAuthenticated_onLockScreen() {
+        // GIVEN not dozing
+        when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+
+        // WHEN we want to unlock collapse
+        mBiometricUnlockController.startWakeAndUnlock(
+                BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
+
+        // THEN we collpase the panels and notify authenticated
+        verify(mShadeController).animateCollapsePanels(
+                /* flags */ anyInt(),
+                /* force */ eq(true),
+                /* delayed */ eq(false),
+                /* speedUpFactor */ anyFloat()
+        );
+        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(
+                /* strongAuth */ eq(false));
+    }
+
+    @Test
     public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() {
         reset(mUpdateMonitor);
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index f34f21b..d098e1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -16,42 +16,80 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.util.BurnInHelperKt;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
     private static final int SCREEN_HEIGHT = 2000;
-    private static final int EMPTY_MARGIN = 0;
     private static final int EMPTY_HEIGHT = 0;
     private static final float ZERO_DRAG = 0.f;
     private static final float OPAQUE = 1.f;
     private static final float TRANSPARENT = 0.f;
+
+    @Mock
+    private Resources mResources;
+
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
     private KeyguardClockPositionAlgorithm.Result mClockPosition;
+
+    private MockitoSession mStaticMockSession;
+    private int mNotificationStackHeight;
+
     private float mPanelExpansion;
+    private int mKeyguardStatusBarHeaderHeight;
     private int mKeyguardStatusHeight;
     private float mDark;
     private float mQsExpansion;
-    private int mCutoutTopInsetPx = 0;
+    private int mCutoutTopInset = 0;
     private boolean mIsSplitShade = false;
+    private float mUdfpsTop = -1;
+    private float mClockBottom = SCREEN_HEIGHT / 2;
+    private boolean mClockTopAligned;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mStaticMockSession = mockitoSession()
+                .mockStatic(BurnInHelperKt.class)
+                .startMocking();
+
         mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
+        when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0);
+        mClockPositionAlgorithm.loadDimens(mResources);
+
         mClockPosition = new KeyguardClockPositionAlgorithm.Result();
     }
 
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
     @Test
     public void clockPositionTopOfScreenOnAOD() {
         // GIVEN on AOD and clock has 0 height
@@ -71,7 +109,7 @@
         // GIVEN on AOD and clock has 0 height
         givenAOD();
         mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mCutoutTopInsetPx = 300;
+        mCutoutTopInset = 300;
         // WHEN the clock position algorithm is run
         positionClock();
         // THEN the clock Y position is below the cutout
@@ -271,6 +309,155 @@
         assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
     }
 
+    @Test
+    public void clockPositionMinimizesBurnInMovementToAvoidUdfpsOnAOD() {
+        // GIVEN a center aligned clock
+        mClockTopAligned = false;
+
+        // GIVEN the clock + udfps are 100px apart
+        mClockBottom = SCREEN_HEIGHT - 500;
+        mUdfpsTop = SCREEN_HEIGHT - 400;
+
+        // GIVEN it's AOD and the burn-in y value is 200
+        givenAOD();
+        givenMaxBurnInOffset(200);
+
+        // WHEN the clock position algorithm is run with the highest burn in offset
+        givenHighestBurnInOffset();
+        positionClock();
+
+        // THEN the worst-case clock Y position is shifted only by 100 (not the full 200),
+        // so that it's at the same location as mUdfpsTop
+        assertThat(mClockPosition.clockY).isEqualTo(100);
+
+        // WHEN the clock position algorithm is run with the lowest burn in offset
+        givenLowestBurnInOffset();
+        positionClock();
+
+        // THEN lowest case starts at 0
+        assertThat(mClockPosition.clockY).isEqualTo(0);
+    }
+
+    @Test
+    public void clockPositionShiftsToAvoidUdfpsOnAOD_usesSpaceAboveClock() {
+        // GIVEN a center aligned clock
+        mClockTopAligned = false;
+
+        // GIVEN there's space at the top of the screen on LS (that's available to be used for
+        // burn-in on AOD)
+        mKeyguardStatusBarHeaderHeight = 150;
+
+        // GIVEN the bottom of the clock is beyond the top of UDFPS
+        mClockBottom = SCREEN_HEIGHT - 300;
+        mUdfpsTop = SCREEN_HEIGHT - 400;
+
+        // GIVEN it's AOD and the burn-in y value is 200
+        givenAOD();
+        givenMaxBurnInOffset(200);
+
+        // WHEN the clock position algorithm is run with the highest burn in offset
+        givenHighestBurnInOffset();
+        positionClock();
+
+        // THEN the algo should shift the clock up and use the area above the clock for
+        // burn-in since the burn in offset > space above clock
+        assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight);
+
+        // WHEN the clock position algorithm is run with the lowest burn in offset
+        givenLowestBurnInOffset();
+        positionClock();
+
+        // THEN lowest case starts at mCutoutTopInset (0 in this case)
+        assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
+    }
+
+    @Test
+    public void clockPositionShiftsToAvoidUdfpsOnAOD_usesMaxBurnInOffset() {
+        // GIVEN a center aligned clock
+        mClockTopAligned = false;
+
+        // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for
+        // burn-in on AOD) but 50px are taken up by the cutout
+        mKeyguardStatusBarHeaderHeight = 200;
+        mCutoutTopInset = 50;
+
+        // GIVEN the bottom of the clock is beyond the top of UDFPS
+        mClockBottom = SCREEN_HEIGHT - 300;
+        mUdfpsTop = SCREEN_HEIGHT - 400;
+
+        // GIVEN it's AOD and the burn-in y value is only 25px (less than space above clock)
+        givenAOD();
+        int maxYBurnInOffset = 25;
+        givenMaxBurnInOffset(maxYBurnInOffset);
+
+        // WHEN the clock position algorithm is run with the highest burn in offset
+        givenHighestBurnInOffset();
+        positionClock();
+
+        // THEN the algo should shift the clock up and use the area above the clock for
+        // burn-in
+        assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight);
+
+        // WHEN the clock position algorithm is run with the lowest burn in offset
+        givenLowestBurnInOffset();
+        positionClock();
+
+        // THEN lowest case starts above mKeyguardStatusBarHeaderHeight
+        assertThat(mClockPosition.clockY).isEqualTo(
+                mKeyguardStatusBarHeaderHeight - 2 * maxYBurnInOffset);
+    }
+
+    @Test
+    public void clockPositionShiftsToMaximizeUdfpsBurnInMovement() {
+        // GIVEN a center aligned clock
+        mClockTopAligned = false;
+
+        // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for
+        // burn-in on AOD) but 50px are taken up by the cutout
+        mKeyguardStatusBarHeaderHeight = 200;
+        mCutoutTopInset = 50;
+        int upperSpaceAvailable = mKeyguardStatusBarHeaderHeight - mCutoutTopInset;
+
+        // GIVEN the bottom of the clock and the top of UDFPS are 100px apart
+        mClockBottom = SCREEN_HEIGHT - 500;
+        mUdfpsTop = SCREEN_HEIGHT - 400;
+        float lowerSpaceAvailable = mUdfpsTop - mClockBottom;
+
+        // GIVEN it's AOD and the burn-in y value is 200
+        givenAOD();
+        givenMaxBurnInOffset(200);
+
+        // WHEN the clock position algorithm is run with the highest burn in offset
+        givenHighestBurnInOffset();
+        positionClock();
+
+        // THEN the algo should shift the clock up and use both the area above
+        // the clock and below the clock (vertically centered in its allowed area)
+        assertThat(mClockPosition.clockY).isEqualTo(
+                (int) (mCutoutTopInset + upperSpaceAvailable + lowerSpaceAvailable));
+
+        // WHEN the clock position algorithm is run with the lowest burn in offset
+        givenLowestBurnInOffset();
+        positionClock();
+
+        // THEN lowest case starts at mCutoutTopInset
+        assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
+    }
+
+    private void givenHighestBurnInOffset() {
+        when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg());
+    }
+
+    private void givenLowestBurnInOffset() {
+        when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(0);
+    }
+
+    private void givenMaxBurnInOffset(int offset) {
+        when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock))
+                .thenReturn(offset);
+        mClockPositionAlgorithm.loadDimens(mResources);
+    }
+
     private void givenAOD() {
         mPanelExpansion = 1.f;
         mDark = 1.f;
@@ -281,12 +468,28 @@
         mDark = 0.f;
     }
 
+    /**
+     * Setup and run the clock position algorithm.
+     *
+     * mClockPosition.clockY will contain the top y-coordinate for the clock position
+     */
     private void positionClock() {
-        mClockPositionAlgorithm.setup(EMPTY_MARGIN, mPanelExpansion, mKeyguardStatusHeight,
-                0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
-                mDark, ZERO_DRAG, false /* bypassEnabled */,
-                0 /* unlockedStackScrollerPadding */, mQsExpansion,
-                mCutoutTopInsetPx, mIsSplitShade);
+        mClockPositionAlgorithm.setup(
+                mKeyguardStatusBarHeaderHeight,
+                mPanelExpansion,
+                mKeyguardStatusHeight,
+                0 /* userSwitchHeight */,
+                0 /* userSwitchPreferredY */,
+                mDark,
+                ZERO_DRAG,
+                false /* bypassEnabled */,
+                0 /* unlockedStackScrollerPadding */,
+                mQsExpansion,
+                mCutoutTopInset,
+                mIsSplitShade,
+                mUdfpsTop,
+                mClockBottom,
+                mClockTopAligned);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 3d887dd..c62d73c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -32,6 +32,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.keyguard.LockIconViewController;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
@@ -96,6 +97,7 @@
     @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    @Mock private LockIconViewController mLockIconViewController;
 
     @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
             mInteractionEventHandlerCaptor;
@@ -141,7 +143,8 @@
                 mNotificationPanelViewController,
                 mStatusBarWindowView,
                 mNotificationStackScrollLayoutController,
-                mStatusBarKeyguardViewManager);
+                mStatusBarKeyguardViewManager,
+                mLockIconViewController);
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar, mNotificationShadeWindowController);
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 751bc81..88a3827 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -140,7 +140,9 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.util.WallpaperController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.MessageRouterImpl;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -256,6 +258,8 @@
     @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
     @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
     @Mock private Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimationLazy;
+    @Mock private Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
+    @Mock private WallpaperController mWallpaperController;
     @Mock private OngoingCallController mOngoingCallController;
     @Mock private SystemStatusAnimationScheduler mAnimationScheduler;
     @Mock private StatusBarLocationPublisher mLocationPublisher;
@@ -431,7 +435,9 @@
                 mBrightnessSliderFactory,
                 mUnfoldTransitionConfig,
                 mUnfoldLightRevealOverlayAnimationLazy,
+                mUnfoldWallpaperController,
                 mMoveFromCenterAnimationLazy,
+                mWallpaperController,
                 mOngoingCallController,
                 mAnimationScheduler,
                 mLocationPublisher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 4476fd8..efe2c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -221,6 +221,18 @@
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
     }
 
+    /** Regression test for b/201097913. */
+    @Test
+    fun onEntryCleanUp_callNotifAddedThenRemoved_listenerNotified() {
+        val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+        notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+        reset(mockOngoingCallListener)
+
+        notifCollectionListener.onEntryCleanUp(ongoingCallNotifEntry)
+
+        verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
+    }
+
     /** Regression test for b/188491504. */
     @Test
     fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 2418243..3c55df9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -124,7 +124,7 @@
         boolean roaming = true;
         MobileDataIndicators indicators = new MobileDataIndicators(
                 status, qs, type, qsType, in, out, typeDescription,
-                typeDescriptionHtml, description, wide, subId, roaming, true);
+                typeDescriptionHtml, description, subId, roaming, true);
         mHandler.setMobileDataIndicators(indicators);
         waitForCallbacks();
 
@@ -141,8 +141,7 @@
         assertEquals(out, expected.activityOut);
         assertEquals(typeDescription, expected.typeContentDescription);
         assertEquals(typeDescriptionHtml, expected.typeContentDescriptionHtml);
-        assertEquals(description, expected.description);
-        assertEquals(wide, expected.isWide);
+        assertEquals(description, expected.qsDescription);
         assertEquals(subId, expected.subId);
         assertTrue(expected.roaming);
         assertTrue(expected.showTriangle);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index e6dc4db..2c461ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -96,8 +96,6 @@
     DumpManager mDumpManager;
     @Mock
     OverlayManagerTransaction.Builder mTransactionBuilder;
-    @Mock
-    Runnable mOnOverlaysApplied;
 
     private ThemeOverlayApplier mManager;
     private boolean mGetOverlayInfoEnabled = true;
@@ -176,7 +174,7 @@
     @Test
     public void allCategoriesSpecified_allEnabledExclusively() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER_HANDLES);
         verify(mOverlayManager).commit(any());
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -188,7 +186,7 @@
     @Test
     public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER_HANDLES);
 
         for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
             if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -205,9 +203,8 @@
     public void allCategoriesSpecified_enabledForAllUserHandles() {
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles, mOnOverlaysApplied);
+                userHandles);
 
-        verify(mOnOverlaysApplied).run();
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
                     eq(TEST_USER.getIdentifier()));
@@ -223,7 +220,7 @@
 
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles, mOnOverlaysApplied);
+                userHandles);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
@@ -237,7 +234,7 @@
                 mock(FabricatedOverlay.class)
         };
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
-                TEST_USER.getIdentifier(), TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER.getIdentifier(), TEST_USER_HANDLES);
 
         for (FabricatedOverlay overlay : pendingCreation) {
             verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -251,7 +248,7 @@
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER_HANDLES);
 
         for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -268,7 +265,7 @@
     @Test
     public void zeroCategoriesSpecified_allDisabled() {
         mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER_HANDLES);
 
         for (String category : THEME_CATEGORIES) {
             verify(mTransactionBuilder).setEnabled(
@@ -283,7 +280,7 @@
         categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES, mOnOverlaysApplied);
+                TEST_USER_HANDLES);
 
         verify(mTransactionBuilder, never()).setEnabled(
                 eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
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 4c282bf..f89bbe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -161,7 +161,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -183,7 +183,7 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM);
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -204,7 +204,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -240,7 +240,7 @@
                 .isFalse();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -270,7 +270,7 @@
                 "android.theme.customization.color_both\":\"0")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -300,7 +300,7 @@
                 "android.theme.customization.color_both\":\"1")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -327,7 +327,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -354,7 +354,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -382,7 +382,7 @@
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -411,14 +411,14 @@
 
 
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
     public void onProfileAdded_setsTheme() {
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -428,7 +428,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -438,7 +438,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -450,7 +450,7 @@
                 Color.valueOf(Color.BLUE), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
 
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
 
         // Regression test: null events should not reset the internal state and allow colors to be
         // applied again.
@@ -458,11 +458,11 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any(), any());
+                any());
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
                 null, null), WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any(), any());
+                any());
     }
 
     @Test
@@ -499,7 +499,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
         clearInvocations(mThemeOverlayApplier);
     }
 
@@ -533,7 +533,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
         clearInvocations(mThemeOverlayApplier);
 
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -542,12 +542,12 @@
 
         // Defers event because we already have initial colors.
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
 
         // Then event happens after setup phase is over.
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
         mDeviceProvisionedListener.getValue().onUserSetupChanged();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -568,11 +568,11 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
 
         mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -592,10 +592,10 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
 
         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -614,7 +614,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
new file mode 100644
index 0000000..3cb19e3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.app.WallpaperInfo
+import android.app.WallpaperManager
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.eq
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doThrow
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class WallpaperControllerTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var wallpaperManager: WallpaperManager
+    @Mock
+    private lateinit var root: View
+    @Mock
+    private lateinit var viewRootImpl: ViewRootImpl
+    @Mock
+    private lateinit var windowToken: IBinder
+
+    @JvmField
+    @Rule
+    val mockitoRule = MockitoJUnit.rule()
+
+    private lateinit var wallaperController: WallpaperController
+
+    @Before
+    fun setup() {
+        `when`(root.viewRootImpl).thenReturn(viewRootImpl)
+        `when`(root.windowToken).thenReturn(windowToken)
+        `when`(root.isAttachedToWindow).thenReturn(true)
+
+        wallaperController = WallpaperController(wallpaperManager)
+
+        wallaperController.rootView = root
+    }
+
+    @Test
+    fun setNotificationShadeZoom_updatesWallpaperManagerZoom() {
+        wallaperController.setNotificationShadeZoom(0.5f)
+
+        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(0.5f))
+    }
+
+    @Test
+    fun setUnfoldTransitionZoom_updatesWallpaperManagerZoom() {
+        wallaperController.setUnfoldTransitionZoom(0.5f)
+
+        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(0.5f))
+    }
+
+    @Test
+    fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
+        wallaperController.onWallpaperInfoUpdated(createWallpaperInfo(
+            useDefaultUnfoldTransition = false
+        ))
+
+        wallaperController.setUnfoldTransitionZoom(0.5f)
+
+        verify(wallpaperManager, never()).setWallpaperZoomOut(any(), anyFloat())
+    }
+
+    @Test
+    fun setUnfoldTransitionZoomAndNotificationShadeZoom_updatesWithMaximumZoom() {
+        wallaperController.setUnfoldTransitionZoom(0.7f)
+        clearInvocations(wallpaperManager)
+
+        wallaperController.setNotificationShadeZoom(0.5f)
+
+        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(0.7f))
+    }
+
+    @Test
+    fun setNotificationShadeZoomAndThenUnfoldTransition_updatesWithMaximumZoom() {
+        wallaperController.setNotificationShadeZoom(0.7f)
+        clearInvocations(wallpaperManager)
+
+        wallaperController.setUnfoldTransitionZoom(0.5f)
+
+        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(0.7f))
+    }
+
+    @Test
+    fun setNotificationZoom_invalidWindow_doesNotSetZoom() {
+        `when`(root.isAttachedToWindow).thenReturn(false)
+
+        verify(wallpaperManager, times(0)).setWallpaperZoomOut(any(), anyFloat())
+    }
+
+    @Test
+    fun setNotificationZoom_exceptionWhenUpdatingZoom_doesNotFail() {
+        doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
+            .setWallpaperZoomOut(any(), anyFloat())
+
+        wallaperController.setNotificationShadeZoom(0.5f)
+
+        verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
+    }
+
+    private fun createWallpaperInfo(useDefaultUnfoldTransition: Boolean = true): WallpaperInfo {
+        val info = mock(WallpaperInfo::class.java)
+        whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultUnfoldTransition)
+        return info
+    }
+}
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index d356894..ef1d581 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -8,6 +8,7 @@
         android:directBootAware="true">
 
         <service android:name=".CameraExtensionsProxyService"
+            android:visibleToInstantApps="true"
             android:exported="true">
         </service>
         <uses-library android:name="androidx.camera.extensions.impl" android:required="false" />
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 9f3045e..3a90a95 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -87,7 +87,7 @@
         mId = sessionId;
         mUid = uid;
         mContentCaptureContext = new ContentCaptureContext(/* clientContext= */ null,
-                activityId, appComponentName, displayId, flags);
+                activityId, appComponentName, displayId, activityToken, flags);
         mSessionStateReceiver = sessionStateReceiver;
         try {
             sessionStateReceiver.asBinder().linkToDeath(() -> onClientDeath(), 0);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index cbc3238..e5716ee 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -107,7 +107,6 @@
         ":display-device-config",
         ":display-layout-config",
         ":device-state-config",
-        ":guiconstants_aidl",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
@@ -150,7 +149,7 @@
         "android.hardware.biometrics.fingerprint-V2.3-java",
         "android.hardware.biometrics.fingerprint-V1-java",
         "android.hardware.oemlock-V1.0-java",
-        "android.hardware.configstore-V1.0-java",
+        "android.hardware.configstore-V1.1-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.soundtrigger-V2.3-java",
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index fdba098..48f5b51 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -476,7 +476,11 @@
      */
     public static void addTombstoneToDropBox(Context ctx, File tombstone, boolean proto) {
         final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
-        final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+        if (db == null) {
+            Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
+            return;
+        }
+
         HashMap<String, Long> timestamps = readTimestamps();
         try {
             if (proto) {
@@ -484,7 +488,7 @@
             } else {
                 final String headers = getBootHeadersToLogAndUpdate();
                 addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
-                        TAG_TOMBSTONE);
+                                 TAG_TOMBSTONE);
             }
         } catch (IOException e) {
             Slog.e(TAG, "Can't log tombstone", e);
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 483250a..68fd0c1 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -92,6 +92,8 @@
 27533 notification_autogrouped (key|3)
 # notification was removed from an autogroup
 275534 notification_unautogrouped (key|3)
+# when a notification is adjusted via assistant
+27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3)
 
 # ---------------------------
 # Watchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index c3543e7..c1c9fbb 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -327,18 +327,23 @@
         }
     }
 
-    private static int getMaxRescueLevel() {
-        return SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)
-                ? LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS : LEVEL_FACTORY_RESET;
+    private static int getMaxRescueLevel(boolean mayPerformFactoryReset) {
+        if (!mayPerformFactoryReset
+                || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+            return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
+        }
+        return LEVEL_FACTORY_RESET;
     }
 
     /**
      * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
      *
      * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.)
+     * @param mayPerformFactoryReset: whether or not a factory reset may be performed for the given
+     *                              failure.
      * @return the rescue level for the n-th mitigation attempt.
      */
-    private static int getRescueLevel(int mitigationCount) {
+    private static int getRescueLevel(int mitigationCount, boolean mayPerformFactoryReset) {
         if (mitigationCount == 1) {
             return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
         } else if (mitigationCount == 2) {
@@ -346,9 +351,9 @@
         } else if (mitigationCount == 3) {
             return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
         } else if (mitigationCount == 4) {
-            return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT);
+            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_WARM_REBOOT);
         } else if (mitigationCount >= 5) {
-            return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET);
+            return Math.min(getMaxRescueLevel(mayPerformFactoryReset), LEVEL_FACTORY_RESET);
         } else {
             Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
             return LEVEL_NONE;
@@ -614,7 +619,8 @@
                 @FailureReasons int failureReason, int mitigationCount) {
             if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                     || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
-                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+                return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+                        mayPerformFactoryReset(failedPackage)));
             } else {
                 return PackageHealthObserverImpact.USER_IMPACT_NONE;
             }
@@ -628,7 +634,8 @@
             }
             if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
                     || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
-                final int level = getRescueLevel(mitigationCount);
+                final int level = getRescueLevel(mitigationCount,
+                        mayPerformFactoryReset(failedPackage));
                 executeRescueLevel(mContext,
                         failedPackage == null ? null : failedPackage.getPackageName(), level);
                 return true;
@@ -653,12 +660,7 @@
             } catch (PackageManager.NameNotFoundException ignore) {
             }
 
-            try {
-                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
-                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
-            } catch (PackageManager.NameNotFoundException e) {
-                return false;
-            }
+            return isPersistentSystemApp(packageName);
         }
 
         @Override
@@ -666,7 +668,7 @@
             if (isDisabled()) {
                 return PackageHealthObserverImpact.USER_IMPACT_NONE;
             }
-            return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+            return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
         }
 
         @Override
@@ -674,7 +676,8 @@
             if (isDisabled()) {
                 return false;
             }
-            executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount));
+            executeRescueLevel(mContext, /*failedPackage=*/ null,
+                    getRescueLevel(mitigationCount, true));
             return true;
         }
 
@@ -683,6 +686,29 @@
             return NAME;
         }
 
+        /**
+         * Returns {@code true} if the failing package is non-null and performing a reboot or
+         * prompting a factory reset is an acceptable mitigation strategy for the package's
+         * failure, {@code false} otherwise.
+         */
+        private boolean mayPerformFactoryReset(@Nullable VersionedPackage failingPackage) {
+            if (failingPackage == null) {
+                return false;
+            }
+
+            return isPersistentSystemApp(failingPackage.getPackageName());
+        }
+
+        private boolean isPersistentSystemApp(@NonNull String packageName) {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+                return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        }
+
         private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
                 @NonNull String namespace) {
             // Record it in calling packages to namespace map
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 91b2440..1176c50 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -420,9 +420,12 @@
             }
 
             synchronized (mLock) {
-                if (mSuppressReminders.containsKey(new Pair<>(sensor, user))) {
+                UserHandle parentUser = UserHandle.of(mUserManagerInternal
+                        .getProfileParentId(user.getIdentifier()));
+                if (mSuppressReminders.containsKey(new Pair<>(sensor, parentUser))) {
                     Log.d(TAG,
-                            "Suppressed sensor privacy reminder for " + packageName + "/" + user);
+                            "Suppressed sensor privacy reminder for " + packageName + "/"
+                                    + parentUser);
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c2926..8727932 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -221,6 +221,9 @@
     @GuardedBy("mLock")
     private final Set<Integer> mFuseMountedUser = new ArraySet<>();
 
+    @GuardedBy("mLock")
+    private final Set<Integer> mCeStoragePreparedUsers = new ArraySet<>();
+
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
 
@@ -4864,5 +4867,19 @@
             }
             return primaryVolumeIds;
         }
+
+        @Override
+        public void markCeStoragePrepared(int userId) {
+            synchronized (mLock) {
+                mCeStoragePreparedUsers.add(userId);
+            }
+        }
+
+        @Override
+        public boolean isCeStoragePrepared(int userId) {
+            synchronized (mLock) {
+                return mCeStoragePreparedUsers.contains(userId);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 85ffaa2..2b1c695 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7736,6 +7736,17 @@
                         } else {
                             killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
                                     "Too many Binders sent to SYSTEM");
+                            // We need to run a GC here, because killing the processes involved
+                            // actually isn't guaranteed to free up the proxies; in fact, if the
+                            // GC doesn't run for a long time, we may even exceed the global
+                            // proxy limit for a process (20000), resulting in system_server itself
+                            // being killed.
+                            // Note that the GC here might not actually clean up all the proxies,
+                            // because the binder reference decrements will come in asynchronously;
+                            // but if new processes belonging to the UID keep adding proxies, we
+                            // will get another callback here, and run the GC again - this time
+                            // cleaning up the old proxies.
+                            VMRuntime.getRuntime().requestConcurrentGC();
                         }
                     }, mHandler);
             t.traceEnd(); // setBinderProxies
@@ -15404,12 +15415,14 @@
         }
 
         @Override
-        public void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, boolean adding,
-                long durationMs, @TempAllowListType int type, @ReasonCode int reasonCode,
-                @Nullable String reason, int callingUid) {
+        public void updateDeviceIdleTempAllowlist(@Nullable int[] appids, int changingUid,
+                boolean adding, long durationMs, @TempAllowListType int type,
+                @ReasonCode int reasonCode, @Nullable String reason, int callingUid) {
             synchronized (ActivityManagerService.this) {
                 synchronized (mProcLock) {
-                    mDeviceIdleTempAllowlist = appids;
+                    if (appids != null) {
+                        mDeviceIdleTempAllowlist = appids;
+                    }
                     if (adding) {
                         if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
                             // Note, the device idle temp-allowlist are by app-ids, but here
@@ -15419,12 +15432,7 @@
                                     callingUid));
                         }
                     } else {
-                        // Note in the removing case, we need to remove all the UIDs matching
-                        // the appId, because DeviceIdle's temp-allowlist are based on AppIds,
-                        // not UIDs.
-                        // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will
-                        // not only remove this app for user 0, but for all users.
-                        mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid));
+                        mFgsStartTempAllowList.removeUid(changingUid);
                     }
                     setAppIdTempAllowlistStateLSP(changingUid, adding);
                 }
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 36c0de9..ad0485b 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1033,6 +1033,7 @@
                     mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
                     state.setProcStateChanged(false);
                 }
+                trimMemoryUiHiddenIfNecessaryLSP(app);
                 if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {
                     if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) {
                         try {
@@ -1075,24 +1076,6 @@
                     }
                     profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                 } else {
-                    if ((curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                                || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
-                        // If this application is now in the background and it
-                        // had done UI, then give it the special trim level to
-                        // have it free UI resources.
-                        final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
-                        if (trimMemoryLevel < level && (thread = app.getThread()) != null) {
-                            try {
-                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
-                                    Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
-                                            + app.processName + " to " + level);
-                                }
-                                thread.scheduleTrimMemory(level);
-                            } catch (RemoteException e) {
-                            }
-                        }
-                        profile.setPendingUiClean(false);
-                    }
                     if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {
                         try {
                             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
@@ -1119,28 +1102,36 @@
                     mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);
                     state.setProcStateChanged(false);
                 }
-                if ((state.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                            || state.isSystemNoUi()) && profile.hasPendingUiClean()) {
-                    if (profile.getTrimMemoryLevel() < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
-                            && (thread = app.getThread()) != null) {
-                        try {
-                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
-                                Slog.v(TAG_OOM_ADJ,
-                                        "Trimming memory of ui hidden " + app.processName
-                                        + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
-                            }
-                            thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    profile.setPendingUiClean(false);
-                }
+                trimMemoryUiHiddenIfNecessaryLSP(app);
                 profile.setTrimMemoryLevel(0);
             });
         }
         return allChanged;
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    private void trimMemoryUiHiddenIfNecessaryLSP(ProcessRecord app) {
+        if ((app.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                || app.mState.isSystemNoUi()) && app.mProfile.hasPendingUiClean()) {
+            // If this application is now in the background and it
+            // had done UI, then give it the special trim level to
+            // have it free UI resources.
+            final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+            IApplicationThread thread;
+            if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {
+                try {
+                    if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+                        Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
+                                + app.processName + " to " + level);
+                    }
+                    thread.scheduleTrimMemory(level);
+                } catch (RemoteException e) {
+                }
+            }
+            app.mProfile.setPendingUiClean(false);
+        }
+    }
+
     @GuardedBy("mProcLock")
     long getLowRamTimeSinceIdleLPr(long now) {
         return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 50278fd..e6ffcfc 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import android.os.Process;
+import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.util.Slog;
 
@@ -38,21 +40,40 @@
     private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
-    @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+    @VisibleForTesting
+    static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+    @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
+            "oom_re_ranking_preserve_top_n_apps";
+    @VisibleForTesting
+    static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
+    @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_USE_FREQUENT_RSS = "oom_re_ranking_rss_use_frequent_rss";
+    @VisibleForTesting
+    static final boolean DEFAULT_USE_FREQUENT_RSS = true;
+    @VisibleForTesting
+    static final String KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS = "oom_re_ranking_rss_update_rate_ms";
+    @VisibleForTesting
+    static final long DEFAULT_RSS_UPDATE_RATE_MS = 10_000; // 10 seconds
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
     @VisibleForTesting
     static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
-    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
+    @VisibleForTesting
+    static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
 
     private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
             new ScoreComparator();
     private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
             new CacheUseComparator();
+    private static final Comparator<RankedProcessRecord> RSS_COMPARATOR =
+            new RssComparator();
     private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
             new LastRssComparator();
     private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
@@ -61,20 +82,33 @@
     private final Object mPhenotypeFlagLock = new Object();
 
     private final ActivityManagerService mService;
+    private final ProcessDependencies mProcessDependencies;
     private final ActivityManagerGlobalLock mProcLock;
     private final Object mProfilerLock;
 
     @GuardedBy("mPhenotypeFlagLock")
     private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    boolean mUseFrequentRss = DEFAULT_USE_FREQUENT_RSS;
+    @GuardedBy("mPhenotypeFlagLock")
+    @VisibleForTesting
+    long mRssUpdateRateMs = DEFAULT_RSS_UPDATE_RATE_MS;
     // Weight to apply to the LRU ordering.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
+    @VisibleForTesting
+    float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
     // Weight to apply to the ordering by number of times the process has been added to the cache.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
+    @VisibleForTesting
+    float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
     // Weight to apply to the ordering by RSS used by the processes.
     @GuardedBy("mPhenotypeFlagLock")
-    @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
+    @VisibleForTesting
+    float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
 
     // Positions to replace in the lru list.
     @GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +127,12 @@
                                 updateUseOomReranking();
                             } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
                                 updateNumberToReRank();
+                            } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
+                                updatePreserveTopNApps();
+                            } else if (KEY_OOM_RE_RANKING_USE_FREQUENT_RSS.equals(name)) {
+                                updateUseFrequentRss();
+                            } else if (KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS.equals(name)) {
+                                updateRssUpdateRateMs();
                             } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
                                 updateLruWeight();
                             } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -106,9 +146,15 @@
             };
 
     CacheOomRanker(final ActivityManagerService service) {
+        this(service, new ProcessDependenciesImpl());
+    }
+
+    @VisibleForTesting
+    CacheOomRanker(final ActivityManagerService service, ProcessDependencies processDependencies) {
         mService = service;
         mProcLock = service.mProcLock;
         mProfilerLock = service.mAppProfiler.mProfilerLock;
+        mProcessDependencies = processDependencies;
     }
 
     /** Load settings from device config and register a listener for changes. */
@@ -160,6 +206,31 @@
     }
 
     @GuardedBy("mPhenotypeFlagLock")
+    private void updatePreserveTopNApps() {
+        int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
+        if (preserveTopNApps < 0) {
+            Slog.w(OomAdjuster.TAG,
+                    "Found negative value for preserveTopNApps, setting to default: "
+                            + preserveTopNApps);
+            preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+        }
+        mPreserveTopNApps = preserveTopNApps;
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateRssUpdateRateMs() {
+        mRssUpdateRateMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS, DEFAULT_RSS_UPDATE_RATE_MS);
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateUseFrequentRss() {
+        mUseFrequentRss = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_OOM_RE_RANKING_USE_FREQUENT_RSS, DEFAULT_USE_FREQUENT_RSS);
+    }
+
+    @GuardedBy("mPhenotypeFlagLock")
     private void updateLruWeight() {
         mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -183,9 +254,39 @@
      */
     @GuardedBy({"mService", "mProcLock"})
     void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
+        // The lruList is a list of processes ordered by how recently they were used. The
+        // least-recently-used apps are at the beginning of the list. We keep track of two
+        // indices in the lruList:
+        //
+        // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
+        // lruList=
+        //   0: app A       ^
+        //   1: app B       | These apps are re-ranked, as they are the first five apps (see
+        //   2: app C       | getNumberToReRank), excluding...
+        //   3: app D       v
+        //   4: app E       ^
+        //   5: app F       | The three most-recently-used apps in the cache (see preserveTopNApps).
+        //   6: app G       v
+        //   7: service A   ^
+        //   8: service B   | Everything beyond lruProcessServiceStart is ignored, as these aren't
+        //   9: service C   | apps.
+        //  10: activity A  |
+        //      ...         |
+        //
+        // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
+        // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
+        // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
+        // happens.
+        //
+        // Note that some apps in the `lruList` can be skipped, if they don't pass
+        //`appCanBeReRanked`.
+
         float lruWeight;
         float usesWeight;
         float rssWeight;
+        int preserveTopNApps;
+        boolean useFrequentRss;
+        long rssUpdateRateMs;
         int[] lruPositions;
         RankedProcessRecord[] scoredProcessRecords;
 
@@ -193,6 +294,9 @@
             lruWeight = mLruWeight;
             usesWeight = mUsesWeight;
             rssWeight = mRssWeight;
+            preserveTopNApps = mPreserveTopNApps;
+            useFrequentRss = mUseFrequentRss;
+            rssUpdateRateMs = mRssUpdateRateMs;
             lruPositions = mLruPositions;
             scoredProcessRecords = mScoredProcessRecords;
         }
@@ -202,52 +306,98 @@
             return;
         }
 
+        int numProcessesEvaluated = 0;
         // Collect the least recently used processes to re-rank, only rank cached
         // processes further down the list than mLruProcessServiceStart.
-        int cachedProcessPos = 0;
-        for (int i = 0; i < lruProcessServiceStart
-                && cachedProcessPos < scoredProcessRecords.length; ++i) {
-            ProcessRecord app = lruList.get(i);
+        int numProcessesReRanked = 0;
+        while (numProcessesEvaluated < lruProcessServiceStart
+                && numProcessesReRanked < scoredProcessRecords.length) {
+            ProcessRecord process = lruList.get(numProcessesEvaluated);
             // Processes that will be assigned a cached oom adj score.
-            if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
-                    >= ProcessList.UNKNOWN_ADJ) {
-                scoredProcessRecords[cachedProcessPos].proc = app;
-                scoredProcessRecords[cachedProcessPos].score = 0.0f;
-                lruPositions[cachedProcessPos] = i;
-                ++cachedProcessPos;
+            if (appCanBeReRanked(process)) {
+                scoredProcessRecords[numProcessesReRanked].proc = process;
+                scoredProcessRecords[numProcessesReRanked].score = 0.0f;
+                lruPositions[numProcessesReRanked] = numProcessesEvaluated;
+                ++numProcessesReRanked;
+            }
+            ++numProcessesEvaluated;
+        }
+
+        // Count how many apps we're not re-ranking (up to preserveTopNApps).
+        int numProcessesNotReRanked = 0;
+        while (numProcessesEvaluated < lruProcessServiceStart
+                && numProcessesNotReRanked < preserveTopNApps) {
+            ProcessRecord process = lruList.get(numProcessesEvaluated);
+            if (appCanBeReRanked(process)) {
+                numProcessesNotReRanked++;
+            }
+            numProcessesEvaluated++;
+        }
+        // Exclude the top `preserveTopNApps` apps from re-ranking.
+        if (numProcessesNotReRanked < preserveTopNApps) {
+            numProcessesReRanked -= preserveTopNApps - numProcessesNotReRanked;
+            if (numProcessesReRanked < 0) {
+                numProcessesReRanked = 0;
             }
         }
 
-        // TODO maybe ensure a certain number above this in the cache before re-ranking.
-        if (cachedProcessPos < scoredProcessRecords.length)  {
-            // Ignore we don't have enough processes to worry about re-ranking.
-            return;
+        if (useFrequentRss) {
+            // Update RSS values for re-ranked apps.
+            long nowMs = SystemClock.elapsedRealtime();
+            for (int i = 0; i < numProcessesReRanked; ++i) {
+                RankedProcessRecord scoredProcessRecord = scoredProcessRecords[i];
+                long sinceUpdateMs =
+                        nowMs - scoredProcessRecord.proc.mState.getCacheOomRankerRssTimeMs();
+                if (scoredProcessRecord.proc.mState.getCacheOomRankerRss() != 0
+                        && sinceUpdateMs < rssUpdateRateMs) {
+                    continue;
+                }
+
+                long[] rss = mProcessDependencies.getRss(scoredProcessRecord.proc.getPid());
+                if (rss == null || rss.length == 0) {
+                    Slog.e(
+                            OomAdjuster.TAG,
+                            "Process.getRss returned bad value, not re-ranking: "
+                                    + Arrays.toString(rss));
+                    return;
+                }
+                // First element is total RSS:
+                // frameworks/base/core/jni/android_util_Process.cpp:1192
+                scoredProcessRecord.proc.mState.setCacheOomRankerRss(rss[0], nowMs);
+                scoredProcessRecord.proc.mProfile.setLastRss(rss[0]);
+            }
         }
 
         // Add scores for each of the weighted features we want to rank based on.
         if (lruWeight > 0.0f) {
             // This doesn't use the LRU list ordering as after the first re-ranking
             // that will no longer be lru.
-            Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
+            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+                    LAST_ACTIVITY_TIME_COMPARATOR);
             addToScore(scoredProcessRecords, lruWeight);
         }
         if (rssWeight > 0.0f) {
-            synchronized (mService.mAppProfiler.mProfilerLock) {
-                Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+            if (useFrequentRss) {
+                Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, RSS_COMPARATOR);
+            } else {
+                synchronized (mService.mAppProfiler.mProfilerLock) {
+                    Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
+                }
             }
             addToScore(scoredProcessRecords, rssWeight);
         }
         if (usesWeight > 0.0f) {
-            Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
+            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
             addToScore(scoredProcessRecords, usesWeight);
         }
 
         // Re-rank by the new combined score.
-        Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
+        Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+                SCORED_PROCESS_RECORD_COMPARATOR);
 
         if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
             boolean printedHeader = false;
-            for (int i = 0; i < scoredProcessRecords.length; ++i) {
+            for (int i = 0; i < numProcessesReRanked; ++i) {
                 if (scoredProcessRecords[i].proc.getPid()
                         != lruList.get(lruPositions[i]).getPid()) {
                     if (!printedHeader) {
@@ -260,12 +410,18 @@
             }
         }
 
-        for (int i = 0; i < scoredProcessRecords.length; ++i) {
+        for (int i = 0; i < numProcessesReRanked; ++i) {
             lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
             scoredProcessRecords[i].proc = null;
         }
     }
 
+    private static boolean appCanBeReRanked(ProcessRecord process) {
+        return !process.isKilledByAm()
+                && process.getThread() != null
+                && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
+    }
+
     private static void addToScore(RankedProcessRecord[] scores, float weight) {
         for (int i = 1; i < scores.length; ++i) {
             scores[i].score += i * weight;
@@ -305,6 +461,16 @@
         }
     }
 
+    private static class RssComparator implements Comparator<RankedProcessRecord> {
+        @Override
+        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
+            // High RSS first to match least recently used.
+            return Long.compare(
+                    o2.proc.mState.getCacheOomRankerRss(),
+                    o1.proc.mState.getCacheOomRankerRss());
+        }
+    }
+
     private static class LastRssComparator implements Comparator<RankedProcessRecord> {
         @Override
         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
@@ -317,4 +483,18 @@
         public ProcessRecord proc;
         public float score;
     }
+
+    /**
+     * Interface for mocking {@link Process} static methods.
+     */
+    interface ProcessDependencies {
+        long[] getRss(int pid);
+    }
+
+    private static class ProcessDependenciesImpl implements ProcessDependencies {
+        @Override
+        public long[] getRss(int pid) {
+            return Process.getRss(pid);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index d4474d6..206dd88 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -343,6 +343,20 @@
     private int mCacheOomRankerUseCount;
 
     /**
+     * Process memory usage (RSS).
+     *
+     * Periodically populated by {@code CacheOomRanker}, stored in this object to cache the values.
+     */
+    @GuardedBy("mService")
+    private long mCacheOomRankerRss;
+
+    /**
+     * The last time, in milliseconds since boot, since {@link #mCacheOomRankerRss} was updated.
+     */
+    @GuardedBy("mService")
+    private long mCacheOomRankerRssTimeMs;
+
+    /**
      * Whether or not this process is reachable from given process.
      */
     @GuardedBy("mService")
@@ -577,6 +591,10 @@
 
     @GuardedBy({"mService", "mProcLock"})
     void setSetProcState(int setProcState) {
+        if (ActivityManager.isProcStateCached(mSetProcState)
+                && !ActivityManager.isProcStateCached(setProcState)) {
+            mCacheOomRankerUseCount++;
+        }
         mSetProcState = setProcState;
     }
 
@@ -840,12 +858,7 @@
 
     @GuardedBy("mService")
     void setCached(boolean cached) {
-        if (mCached != cached) {
-            mCached = cached;
-            if (cached) {
-                ++mCacheOomRankerUseCount;
-            }
-        }
+        mCached = cached;
     }
 
     @GuardedBy("mService")
@@ -1151,6 +1164,21 @@
         return mLastInvisibleTime;
     }
 
+    public void setCacheOomRankerRss(long rss, long rssTimeMs) {
+        mCacheOomRankerRss = rss;
+        mCacheOomRankerRssTimeMs = rssTimeMs;
+    }
+
+    @GuardedBy("mService")
+    public long getCacheOomRankerRss() {
+        return mCacheOomRankerRss;
+    }
+
+    @GuardedBy("mService")
+    public long getCacheOomRankerRssTimeMs() {
+        return mCacheOomRankerRssTimeMs;
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     void dump(PrintWriter pw, String prefix, long nowUptime) {
         if (mReportedInteraction || mFgInteractionTime != 0) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 804e442..17930ea 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -927,9 +927,9 @@
     }
 
     public void postNotification() {
-        final int appUid = appInfo.uid;
-        final int appPid = app.getPid();
-        if (isForeground && foregroundNoti != null) {
+        if (isForeground && foregroundNoti != null && app != null) {
+            final int appUid = appInfo.uid;
+            final int appPid = app.getPid();
             // Do asynchronous communication with notification manager to
             // avoid deadlocks.
             final String localPackageName = packageName;
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index db2ecc5..19dcee4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -696,7 +696,7 @@
                 idpw.print("User Level Hibernation States, ");
                 idpw.printPair("user", userId);
                 idpw.println();
-                Map<String, UserLevelState> stateMap = mUserStates.get(i);
+                Map<String, UserLevelState> stateMap = mUserStates.get(userId);
                 idpw.increaseIndent();
                 for (UserLevelState state : stateMap.values()) {
                     idpw.print(state);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5d0bad2..8c19284 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -243,7 +243,7 @@
      */
     private static final int FLAG_ADJUST_VOLUME = 1;
 
-    private final Context mContext;
+    final Context mContext;
     private final ContentResolver mContentResolver;
     private final AppOpsManager mAppOps;
 
@@ -318,6 +318,8 @@
     private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
     private static final int MSG_DISPATCH_AUDIO_MODE = 40;
     private static final int MSG_ROUTING_UPDATED = 41;
+    private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
+    private static final int MSG_PERSIST_SPATIAL_AUDIO_ENABLED = 43;
 
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -1037,12 +1039,13 @@
 
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
 
+        mHasSpatializerEffect = SystemProperties.getBoolean("ro.audio.spatializer_enabled", false);
+
         // done with service initialization, continue additional work in our Handler thread
         queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
                 0 /* arg1 */,  0 /* arg2 */, null /* obj */,  0 /* delay */);
         queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
-                0 /* arg1 */,  0 /* arg2 */, null /* obj */,  0 /* delay */);
-
+                0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
     }
 
     /**
@@ -1236,6 +1239,9 @@
     // routing monitoring from AudioSystemAdapter
     @Override
     public void onRoutingUpdatedFromNative() {
+        if (!mHasSpatializerEffect) {
+            return;
+        }
         sendMsg(mAudioHandler,
                 MSG_ROUTING_UPDATED,
                 SENDMSG_REPLACE, 0, 0, null,
@@ -1432,8 +1438,10 @@
             }
         }
 
-        // TODO check property if feature enabled
-        mSpatializerHelper.reset(/* featureEnabled */ SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+        if (mHasSpatializerEffect) {
+            mSpatializerHelper.reset(/* featureEnabled */ isSpatialAudioEnabled());
+            monitorRoutingChanges(true);
+        }
 
         onIndicateSystemReady();
         // indicate the end of reconfiguration phase to audio HAL
@@ -7569,12 +7577,18 @@
                     break;
 
                 case MSG_INIT_SPATIALIZER:
-                    mSpatializerHelper.init();
-                    // TODO read property to see if enabled
-                    mSpatializerHelper.setFeatureEnabled(SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+                    mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect);
+                    if (mHasSpatializerEffect) {
+                        mSpatializerHelper.setFeatureEnabled(isSpatialAudioEnabled());
+                        monitorRoutingChanges(true);
+                    }
                     mAudioEventWakeLock.release();
                     break;
 
+                case MSG_INIT_HEADTRACKING_SENSORS:
+                    mSpatializerHelper.onInitSensors(/*init*/ msg.arg1 == 1);
+                    break;
+
                 case MSG_CHECK_MUSIC_ACTIVE:
                     onCheckMusicActive((String) msg.obj);
                     break;
@@ -7711,6 +7725,10 @@
                 case MSG_ROUTING_UPDATED:
                     mSpatializerHelper.onRoutingUpdated();
                     break;
+
+                case MSG_PERSIST_SPATIAL_AUDIO_ENABLED:
+                    onPersistSpatialAudioEnabled(msg.arg1 == 1);
+                    break;
             }
         }
     }
@@ -8280,7 +8298,40 @@
 
     //==========================================================================================
     private final @NonNull SpatializerHelper mSpatializerHelper;
-    private static final boolean SPATIALIZER_FEATURE_ENABLED_DEFAULT = false;
+    /**
+     * Initialized from property ro.audio.spatializer_enabled
+     * Should only be 1 when the device ships with a Spatializer effect
+     */
+    private final boolean mHasSpatializerEffect;
+    /**
+     * Default value for the spatial audio feature
+     */
+    private static final boolean SPATIAL_AUDIO_ENABLED_DEFAULT = true;
+
+    /**
+     * persist in user settings whether the feature is enabled.
+     * Can change when {@link Spatializer#setEnabled(boolean)} is called and successfully
+     * changes the state of the feature
+     * @param featureEnabled
+     */
+    void persistSpatialAudioEnabled(boolean featureEnabled) {
+        sendMsg(mAudioHandler,
+                MSG_PERSIST_SPATIAL_AUDIO_ENABLED,
+                SENDMSG_REPLACE, featureEnabled ? 1 : 0, 0, null,
+                /*delay ms*/ 100);
+    }
+
+    void onPersistSpatialAudioEnabled(boolean enabled) {
+        Settings.Secure.putIntForUser(mContentResolver,
+                Settings.Secure.SPATIAL_AUDIO_ENABLED, enabled ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+    boolean isSpatialAudioEnabled() {
+        return Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.SPATIAL_AUDIO_ENABLED, SPATIAL_AUDIO_ENABLED_DEFAULT ? 1 : 0,
+                UserHandle.USER_CURRENT) == 1;
+    }
 
     private void enforceModifyDefaultAudioEffectsPermission() {
         if (mContext.checkCallingOrSelfPermission(
@@ -8433,6 +8484,32 @@
         mSpatializerHelper.setDesiredHeadTrackingMode(mode);
     }
 
+    /** @see Spatializer#setEffectParameter */
+    public void setSpatializerParameter(int key, @NonNull byte[] value) {
+        enforceModifyDefaultAudioEffectsPermission();
+        Objects.requireNonNull(value);
+        mSpatializerHelper.setEffectParameter(key, value);
+    }
+
+    /** @see Spatializer#getEffectParameter */
+    public void getSpatializerParameter(int key, @NonNull byte[] value) {
+        enforceModifyDefaultAudioEffectsPermission();
+        Objects.requireNonNull(value);
+        mSpatializerHelper.getEffectParameter(key, value);
+    }
+
+    /**
+     * post a message to schedule init/release of head tracking sensors
+     * @param init initialization if true, release if false
+     */
+    void postInitSpatializerHeadTrackingSensors(boolean init) {
+        sendMsg(mAudioHandler,
+                MSG_INIT_HEADTRACKING_SENSORS,
+                SENDMSG_REPLACE,
+                /*arg1*/ init ? 1 : 0,
+                0, TAG, /*delay*/ 0);
+    }
+
     //==========================================================================================
     private boolean readCameraSoundForced() {
         return SystemProperties.getBoolean("audio.camerasound.force", false) ||
@@ -9013,6 +9090,12 @@
         sVolumeLogger.dump(pw);
         pw.println("\n");
         dumpSupportedSystemUsage(pw);
+
+        pw.println("\n");
+        pw.println("\nSpatial audio:");
+        pw.println("mHasSpatializerEffect:" + mHasSpatializerEffect);
+        pw.println("isSpatializerEnabled:" + isSpatializerEnabled());
+        pw.println("isSpatialAudioEnabled:" + isSpatialAudioEnabled());
     }
 
     private void dumpSupportedSystemUsage(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java
index bb627e5..00cb280 100644
--- a/services/core/java/com/android/server/audio/FadeOutManager.java
+++ b/services/core/java/com/android/server/audio/FadeOutManager.java
@@ -36,7 +36,16 @@
 
     public static final String TAG = "AudioService.FadeOutManager";
 
+    /** duration of the fade out curve */
     /*package*/ static final long FADE_OUT_DURATION_MS = 2000;
+    /**
+     * delay after which a faded out player will be faded back in. This will be heard by the user
+     * only in the case of unmuting players that didn't respect audio focus and didn't stop/pause
+     * when their app lost focus.
+     * This is the amount of time between the app being notified of
+     * the focus loss (when its muted by the fade out), and the time fade in (to unmute) starts
+     */
+    /*package*/ static final long DELAY_FADE_IN_OFFENDERS_MS = 2000;
 
     private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG;
 
@@ -148,6 +157,11 @@
         }
     }
 
+    /**
+     * Remove the app for the given UID from the list of faded out apps, unfade out its players
+     * @param uid the uid for the app to unfade out
+     * @param players map of current available players (so we can get an APC from piid)
+     */
     synchronized void unfadeOutUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
         Log.i(TAG, "unfadeOutUid() uid:" + uid);
         final FadedOutApp fa = mFadedApps.remove(uid);
@@ -157,12 +171,6 @@
         fa.removeUnfadeAll(players);
     }
 
-    synchronized void forgetUid(int uid) {
-        //Log.v(TAG, "forget() uid:" + uid);
-        //mFadedApps.remove(uid);
-        // TODO unfade all players later in case they are reused or the app continued to play
-    }
-
     // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
     //   see {@link PlaybackActivityMonitor#playerEvent}
     synchronized void checkFade(@NonNull AudioPlaybackConfiguration apc) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa..9548ada 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -131,6 +131,11 @@
     @Override
     public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
         mFocusEnforcer.restoreVShapedPlayers(winner);
+        // remove scheduled events to unfade out offending players (if any) corresponding to
+        // this uid, as we're removing any effects of muting/ducking/fade out now
+        mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID,
+                new ForgetFadeUidInfo(winner.getClientUid()));
+
     }
 
     @Override
@@ -1182,6 +1187,13 @@
                 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
                 FadeOutManager.FADE_OUT_DURATION_MS);
     }
+
+    private void postForgetUidLater(int uid) {
+        mFocusHandler.sendMessageDelayed(
+                mFocusHandler.obtainMessage(MSL_L_FORGET_UID, new ForgetFadeUidInfo(uid)),
+                FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS);
+    }
+
     //=================================================================
     // Message handling
     private Handler mFocusHandler;
@@ -1196,6 +1208,8 @@
      */
     private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
 
+    private static final int MSL_L_FORGET_UID = 2;
+
     private void initFocusThreading() {
         mFocusThread = new HandlerThread(TAG);
         mFocusThread.start();
@@ -1213,15 +1227,56 @@
                             if (loser.isInFocusLossLimbo()) {
                                 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
                                 loser.release();
-                                mFocusEnforcer.forgetUid(loser.getClientUid());
+                                postForgetUidLater(loser.getClientUid());
                             }
                         }
                         break;
+
+                    case MSL_L_FORGET_UID:
+                        final int uid = ((ForgetFadeUidInfo) msg.obj).mUid;
+                        if (DEBUG) {
+                            Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid);
+                        }
+                        mFocusEnforcer.forgetUid(uid);
+                        break;
                     default:
                         break;
                 }
             }
         };
+    }
 
+    /**
+     * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior.
+     * Having a class with an equals() override allows using Handler.removeEqualsMessage() to
+     * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid"
+     * whenever a new, more recent, focus related event happens before this one is handled.
+     */
+    private static final class ForgetFadeUidInfo {
+        private final int mUid;
+
+        ForgetFadeUidInfo(int uid) {
+            mUid = uid;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o;
+            if (f.mUid != mUid) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return mUid;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a13b2eb..b94cea4 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -747,7 +747,11 @@
 
     @Override
     public void forgetUid(int uid) {
-        mFadingManager.forgetUid(uid);
+        final HashMap<Integer, AudioPlaybackConfiguration> players;
+        synchronized (mPlayerLock) {
+            players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
+        }
+        mFadingManager.unfadeOutUid(uid, players);
     }
 
     //=================================================================
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1d85974..b2fa86b 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -18,6 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioFormat;
@@ -26,7 +29,9 @@
 import android.media.ISpatializer;
 import android.media.ISpatializerCallback;
 import android.media.ISpatializerHeadToSoundStagePoseCallback;
+import android.media.ISpatializerHeadTrackingCallback;
 import android.media.ISpatializerHeadTrackingModeCallback;
+import android.media.SpatializationLevel;
 import android.media.Spatializer;
 import android.media.SpatializerHeadTrackingMode;
 import android.os.RemoteCallbackList;
@@ -44,6 +49,7 @@
 
     private static final String TAG = "AS.SpatializerHelper";
     private static final boolean DEBUG = true;
+    private static final boolean DEBUG_MORE = false;
 
     private static void logd(String s) {
         if (DEBUG) {
@@ -53,6 +59,7 @@
 
     private final @NonNull AudioSystemAdapter mASA;
     private final @NonNull AudioService mAudioService;
+    private @Nullable SensorManager mSensorManager;
 
     //------------------------------------------------------------
     // Spatializer state machine
@@ -71,6 +78,8 @@
     private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
     private @Nullable ISpatializer mSpat;
     private @Nullable SpatializerCallback mSpatCallback;
+    private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback;
+
 
     // default attributes and format that determine basic availability of spatialization
     private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
@@ -97,8 +106,13 @@
         mASA = asa;
     }
 
-    synchronized void init() {
+    synchronized void init(boolean effectExpected) {
         Log.i(TAG, "Initializing");
+        if (!effectExpected) {
+            Log.i(TAG, "Setting state to STATE_NOT_SUPPORTED due to effect not expected");
+            mState = STATE_NOT_SUPPORTED;
+            return;
+        }
         if (mState != STATE_UNINITIALIZED) {
             throw new IllegalStateException(("init() called in state:" + mState));
         }
@@ -124,7 +138,7 @@
             for (byte level : levels) {
                 logd("found support for level: " + level);
                 if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL) {
-                    logd("Setting Spatializer to LEVEL_MULTICHANNEL");
+                    logd("Setting capable level to LEVEL_MULTICHANNEL");
                     mCapableSpatLevel = level;
                     break;
                 }
@@ -156,7 +170,7 @@
         mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
         mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
         mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
-        init();
+        init(true);
         setFeatureEnabled(featureEnabled);
     }
 
@@ -188,13 +202,24 @@
         public void onLevelChanged(byte level) {
             logd("SpatializerCallback.onLevelChanged level:" + level);
             synchronized (SpatializerHelper.this) {
-                mSpatLevel = level;
+                mSpatLevel = spatializationLevelToSpatializerInt(level);
             }
             // TODO use reported spat level to change state
-        }
 
+            // init sensors
+            if (level == SpatializationLevel.NONE) {
+                initSensors(/*init*/false);
+            } else {
+                postInitSensors(true);
+            }
+        }
+    };
+
+    // spatializer head tracking callback from native
+    private final class SpatializerHeadTrackingCallback
+            extends ISpatializerHeadTrackingCallback.Stub {
         public void onHeadTrackingModeChanged(byte mode)  {
-            logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + mode);
+            logd("SpatializerHeadTrackingCallback.onHeadTrackingModeChanged mode:" + mode);
             int oldMode, newMode;
             synchronized (this) {
                 oldMode = mActualHeadTrackingMode;
@@ -208,21 +233,22 @@
 
         public void onHeadToSoundStagePoseUpdated(float[] headToStage)  {
             if (headToStage == null) {
-                Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
+                Log.e(TAG, "SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated"
+                        + "null transform");
                 return;
             }
             if (headToStage.length != 6) {
-                Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated invalid transform length"
-                        + headToStage.length);
+                Log.e(TAG, "SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated"
+                        + " invalid transform length" + headToStage.length);
                 return;
             }
-            if (DEBUG) {
+            if (DEBUG_MORE) {
                 // 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
                 StringBuilder t = new StringBuilder(42);
                 for (float val : headToStage) {
                     t.append("[").append(String.format(Locale.ENGLISH, "%.3f", val)).append("]");
                 }
-                logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
+                logd("SpatializerHeadTrackingCallback.onHeadToStagePoseUpdated headToStage:" + t);
             }
             dispatchPoseUpdate(headToStage);
         }
@@ -370,7 +396,7 @@
             }
         }
         mStateCallbacks.finishBroadcast();
-        // TODO persist enabled state
+        mAudioService.persistSpatialAudioEnabled(featureEnabled);
     }
 
     private synchronized void setDispatchAvailableState(boolean available) {
@@ -433,9 +459,14 @@
     private void createSpat() {
         if (mSpat == null) {
             mSpatCallback = new SpatializerCallback();
+            mSpatHeadTrackingCallback = new SpatializerHeadTrackingCallback();
             mSpat = AudioSystem.getSpatializer(mSpatCallback);
             try {
                 mSpat.setLevel((byte)  Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+                //TODO: register heatracking callback only when sensors are registered
+                if (mSpat.isHeadTrackingSupported()) {
+                    mSpat.registerHeadTrackingCallback(mSpatHeadTrackingCallback);
+                }
             } catch (RemoteException e) {
                 Log.e(TAG, "Can't set spatializer level", e);
                 mState = STATE_NOT_SUPPORTED;
@@ -451,6 +482,7 @@
         if (mSpat != null) {
             mSpatCallback = null;
             try {
+                mSpat.registerHeadTrackingCallback(null);
                 mSpat.release();
                 mSpat = null;
             } catch (RemoteException e) {
@@ -627,36 +659,6 @@
         }
     }
 
-    private int headTrackingModeTypeToSpatializerInt(byte mode) {
-        switch (mode) {
-            case SpatializerHeadTrackingMode.OTHER:
-                return Spatializer.HEAD_TRACKING_MODE_OTHER;
-            case SpatializerHeadTrackingMode.DISABLED:
-                return Spatializer.HEAD_TRACKING_MODE_DISABLED;
-            case SpatializerHeadTrackingMode.RELATIVE_WORLD:
-                return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
-            case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
-                return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
-            default:
-                throw(new IllegalArgumentException("Unexpected head tracking mode:" + mode));
-        }
-    }
-
-    private byte spatializerIntToHeadTrackingModeType(int sdkMode) {
-        switch (sdkMode) {
-            case Spatializer.HEAD_TRACKING_MODE_OTHER:
-                return SpatializerHeadTrackingMode.OTHER;
-            case Spatializer.HEAD_TRACKING_MODE_DISABLED:
-                return SpatializerHeadTrackingMode.DISABLED;
-            case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
-                return SpatializerHeadTrackingMode.RELATIVE_WORLD;
-            case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
-                return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
-            default:
-                throw(new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
-        }
-    }
-
     private boolean checkSpatForHeadTracking(String funcName) {
         switch (mState) {
             case STATE_UNINITIALIZED:
@@ -728,4 +730,151 @@
         }
         mHeadPoseCallbacks.finishBroadcast();
     }
+
+    //------------------------------------------------------
+    // vendor parameters
+    synchronized void setEffectParameter(int key, @NonNull byte[] value) {
+        switch (mState) {
+            case STATE_UNINITIALIZED:
+            case STATE_NOT_SUPPORTED:
+                throw (new IllegalStateException(
+                        "Can't set parameter key:" + key + " without a spatializer"));
+            case STATE_ENABLED_UNAVAILABLE:
+            case STATE_DISABLED_UNAVAILABLE:
+            case STATE_DISABLED_AVAILABLE:
+            case STATE_ENABLED_AVAILABLE:
+                if (mSpat == null) {
+                    throw (new IllegalStateException(
+                            "null Spatializer for setParameter for key:" + key));
+                }
+                break;
+        }
+        // mSpat != null
+        try {
+            mSpat.setParameter(key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setParameter for key:" + key, e);
+        }
+    }
+
+    synchronized void getEffectParameter(int key, @NonNull byte[] value) {
+        switch (mState) {
+            case STATE_UNINITIALIZED:
+            case STATE_NOT_SUPPORTED:
+                throw (new IllegalStateException(
+                        "Can't get parameter key:" + key + " without a spatializer"));
+            case STATE_ENABLED_UNAVAILABLE:
+            case STATE_DISABLED_UNAVAILABLE:
+            case STATE_DISABLED_AVAILABLE:
+            case STATE_ENABLED_AVAILABLE:
+                if (mSpat == null) {
+                    throw (new IllegalStateException(
+                            "null Spatializer for getParameter for key:" + key));
+                }
+                break;
+        }
+        // mSpat != null
+        try {
+            mSpat.getParameter(key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getParameter for key:" + key, e);
+        }
+    }
+
+    //------------------------------------------------------
+    // sensors
+    private void initSensors(boolean init) {
+        if (mSensorManager == null) {
+            mSensorManager = (SensorManager)
+                    mAudioService.mContext.getSystemService(Context.SENSOR_SERVICE);
+        }
+        final int headHandle;
+        final int screenHandle;
+        if (init) {
+            if (mSensorManager == null) {
+                Log.e(TAG, "Null SensorManager, can't init sensors");
+                return;
+            }
+            // TODO replace with dynamic association of sensor for headtracker
+            Sensor headSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
+            headHandle = headSensor.getHandle();
+            //Sensor screenSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
+            //screenHandle = deviceSensor.getHandle();
+            screenHandle = -1;
+        } else {
+            // -1 is disable value
+            screenHandle = -1;
+            headHandle = -1;
+        }
+        try {
+            Log.i(TAG, "setScreenSensor:" + screenHandle);
+            mSpat.setScreenSensor(screenHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Error calling setScreenSensor:" + screenHandle, e);
+        }
+        try {
+            Log.i(TAG, "setHeadSensor:" + headHandle);
+            mSpat.setHeadSensor(headHandle);
+        } catch (Exception e) {
+            Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e);
+        }
+    }
+
+    private void postInitSensors(boolean init) {
+        mAudioService.postInitSpatializerHeadTrackingSensors(init);
+    }
+
+    synchronized void onInitSensors(boolean init) {
+        final int[] modes = getSupportedHeadTrackingModes();
+        if (modes.length == 0) {
+            Log.i(TAG, "not initializing sensors, no headtracking supported");
+            return;
+        }
+        initSensors(init);
+    }
+
+    //------------------------------------------------------
+    // SDK <-> AIDL converters
+    private static int headTrackingModeTypeToSpatializerInt(byte mode) {
+        switch (mode) {
+            case SpatializerHeadTrackingMode.OTHER:
+                return Spatializer.HEAD_TRACKING_MODE_OTHER;
+            case SpatializerHeadTrackingMode.DISABLED:
+                return Spatializer.HEAD_TRACKING_MODE_DISABLED;
+            case SpatializerHeadTrackingMode.RELATIVE_WORLD:
+                return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
+            case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+                return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
+            default:
+                throw(new IllegalArgumentException("Unexpected head tracking mode:" + mode));
+        }
+    }
+
+    private static byte spatializerIntToHeadTrackingModeType(int sdkMode) {
+        switch (sdkMode) {
+            case Spatializer.HEAD_TRACKING_MODE_OTHER:
+                return SpatializerHeadTrackingMode.OTHER;
+            case Spatializer.HEAD_TRACKING_MODE_DISABLED:
+                return SpatializerHeadTrackingMode.DISABLED;
+            case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
+                return SpatializerHeadTrackingMode.RELATIVE_WORLD;
+            case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
+                return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
+            default:
+                throw(new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
+        }
+    }
+
+    private static int spatializationLevelToSpatializerInt(byte level) {
+        switch (level) {
+            case SpatializationLevel.NONE:
+                return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+            case SpatializationLevel.SPATIALIZER_MULTICHANNEL:
+                return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL;
+            case SpatializationLevel.SPATIALIZER_MCHAN_BED_PLUS_OBJECTS:
+                return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS;
+            default:
+                throw(new IllegalArgumentException("Unexpected spatializer level:" + level));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 8a11e8d..ca051e9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -167,14 +167,17 @@
     @Override
     protected void stopHalOperation() {
         mSensorOverlays.hide(getSensorId());
-
-        try {
-            mCancellationSignal.cancel();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception", e);
-            onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                    0 /* vendorCode */);
-            mCallback.onClientFinished(this, false /* success */);
+        if (mCancellationSignal != null) {
+            try {
+                mCancellationSignal.cancel();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+                onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                mCallback.onClientFinished(this, false /* success */);
+            }
+        } else {
+            Slog.e(TAG, "cancellation signal was null");
         }
     }
 
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index f6ba369..b5846b5 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -183,14 +183,15 @@
      */
     synchronized boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
             @Nullable Long versionCode) {
+        if (packageName == null) {
+            return false;
+        }
         boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
-
         // If the app is not installed or no longer has raw overrides, evaluate to false
         if (versionCode == null || !mRawOverrides.containsKey(packageName) || !allowed) {
             removePackageOverrideInternal(packageName);
             return false;
         }
-
         // Evaluate the override based on its version
         int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode);
         switch (overrideValue) {
@@ -266,6 +267,9 @@
      * @return {@code true} if the change should be enabled for the package.
      */
     boolean willBeEnabled(String packageName) {
+        if (packageName == null) {
+            return defaultValue();
+        }
         final PackageOverride override = mRawOverrides.get(packageName);
         if (override != null) {
             switch (override.evaluateForAllVersions()) {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 806a5dd..792feea 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateInfo;
 import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManagerInternal;
 import android.hardware.devicestate.IDeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 import android.os.Binder;
@@ -161,6 +162,7 @@
     @Override
     public void onStart() {
         publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+        publishLocalService(DeviceStateManagerInternal.class, new LocalService());
     }
 
     @VisibleForTesting
@@ -240,13 +242,6 @@
     }
 
     /** Returns the list of currently supported device state identifiers. */
-    private int[] getSupportedStateIdentifiers() {
-        synchronized (mLock) {
-            return getSupportedStateIdentifiersLocked();
-        }
-    }
-
-    /** Returns the list of currently supported device state identifiers. */
     private int[] getSupportedStateIdentifiersLocked() {
         int[] supportedStates = new int[mDeviceStates.size()];
         for (int i = 0; i < supportedStates.length; i++) {
@@ -848,4 +843,14 @@
             }
         }
     }
+
+    /** Implementation of {@link DeviceStateManagerInternal} published as a local service. */
+    private final class LocalService extends DeviceStateManagerInternal {
+        @Override
+        public int[] getSupportedStateIdentifiers() {
+            synchronized (mLock) {
+                return getSupportedStateIdentifiersLocked();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 827523b..f16ed41 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -53,6 +53,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManagerInternal;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
@@ -131,6 +132,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
@@ -210,6 +212,7 @@
     private WindowManagerInternal mWindowManagerInternal;
     private InputManagerInternal mInputManagerInternal;
     private IMediaProjectionManager mProjectionService;
+    private DeviceStateManagerInternal mDeviceStateManager;
     private int[] mUserDisabledHdrTypes = {};
     private boolean mAreUserDisabledHdrTypesAllowed = true;
 
@@ -557,10 +560,9 @@
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
 
-            DeviceStateManager deviceStateManager =
-                    mContext.getSystemService(DeviceStateManager.class);
-            deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
-                    new DeviceStateListener());
+            mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
+            mContext.getSystemService(DeviceStateManager.class).registerCallback(
+                    new HandlerExecutor(mHandler), new DeviceStateListener());
 
             scheduleTraversalLocked(false);
         }
@@ -3274,6 +3276,53 @@
         }
 
         @Override
+        public Set<DisplayInfo> getPossibleDisplayInfo(int displayId) {
+            synchronized (mSyncRoot) {
+                // Retrieve the group associated with this display id.
+                final int displayGroupId =
+                        mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(displayId);
+                if (displayGroupId == Display.INVALID_DISPLAY_GROUP) {
+                    Slog.w(TAG,
+                            "Can't get possible display info since display group for " + displayId
+                                    + " does not exist");
+                    return new ArraySet<>();
+                }
+
+                // Assume any display in this group can be swapped out for the given display id.
+                Set<DisplayInfo> possibleInfo = new ArraySet<>();
+                final DisplayGroup group = mLogicalDisplayMapper.getDisplayGroupLocked(
+                        displayGroupId);
+                for (int i = 0; i < group.getSizeLocked(); i++) {
+                    final int id = group.getIdLocked(i);
+                    final LogicalDisplay logical = mLogicalDisplayMapper.getDisplayLocked(id);
+                    if (logical == null) {
+                        Slog.w(TAG,
+                                "Can't get possible display info since logical display for "
+                                        + "display id " + id + " does not exist, as part of group "
+                                        + displayGroupId);
+                    } else {
+                        possibleInfo.add(logical.getDisplayInfoLocked());
+                    }
+                }
+
+                // For the supported device states, retrieve the DisplayInfos for the logical
+                // display layout.
+                if (mDeviceStateManager == null) {
+                    Slog.w(TAG, "Can't get supported states since DeviceStateManager not ready");
+                } else {
+                    final int[] supportedStates =
+                            mDeviceStateManager.getSupportedStateIdentifiers();
+                    for (int state : supportedStates) {
+                        possibleInfo.addAll(
+                                mLogicalDisplayMapper.getDisplayInfoForStateLocked(state, displayId,
+                                        displayGroupId));
+                    }
+                }
+                return possibleInfo;
+            }
+        }
+
+        @Override
         public Point getDisplayPosition(int displayId) {
             synchronized (mSyncRoot) {
                 final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index abbe13a..1063481 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -721,13 +721,13 @@
         final IBinder token = device.getDisplayTokenLocked();
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         mHandler.post(() -> {
-            if (mDisplayDevice == device) {
-                return;
+            if (mDisplayDevice != device) {
+                mDisplayDevice = device;
+                mUniqueDisplayId = uniqueId;
+                mDisplayDeviceConfig = config;
+                loadFromDisplayDeviceConfig(token, info);
+                updatePowerState();
             }
-            mDisplayDevice = device;
-            mUniqueDisplayId = uniqueId;
-            mDisplayDeviceConfig = config;
-            loadFromDisplayDeviceConfig(token, info);
         });
     }
 
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index a931718..0fbc3e8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -22,8 +22,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -38,6 +41,7 @@
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -67,7 +71,7 @@
     public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
     public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
 
-    private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
+    private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 300;
 
     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
 
@@ -96,6 +100,11 @@
     private final boolean mSupportsConcurrentInternalDisplays;
 
     /**
+     * Wake the device when transitioning into this device state.
+     */
+    private final int mDeviceStateOnWhichToWakeUp;
+
+    /**
      * Map of all logical displays indexed by logical display id.
      * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
      * TODO: multi-display - Move the aforementioned comment?
@@ -111,6 +120,7 @@
     private final Listener mListener;
     private final DisplayManagerService.SyncRoot mSyncRoot;
     private final LogicalDisplayMapperHandler mHandler;
+    private final PowerManager mPowerManager;
 
     /**
      * Has an entry for every logical display that the rest of the system has been notified about.
@@ -148,12 +158,15 @@
             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
             @NonNull Handler handler) {
         mSyncRoot = syncRoot;
+        mPowerManager = context.getSystemService(PowerManager.class);
         mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
         mDisplayDeviceRepo = repo;
         mListener = listener;
         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
         mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
+        mDeviceStateOnWhichToWakeUp = context.getResources().getInteger(
+                com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp);
         mDisplayDeviceRepo.addListener(this);
         mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
     }
@@ -254,6 +267,61 @@
         return mDisplayGroups.get(groupId);
     }
 
+    /**
+     * Returns the set of {@link DisplayInfo} for this device state, only fetching the info that is
+     * part of the same display group as the provided display id. The DisplayInfo represent the
+     * logical display layouts possible for the given device state.
+     *
+     * @param deviceState the state to query possible layouts for
+     * @param displayId   the display id to apply to all displays within the group
+     * @param groupId     the display group to filter display info for. Must be the same group as
+     *                    the display with the provided display id.
+     */
+    public Set<DisplayInfo> getDisplayInfoForStateLocked(int deviceState, int displayId,
+            int groupId) {
+        Set<DisplayInfo> displayInfos = new ArraySet<>();
+        final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
+        final int layoutSize = layout.size();
+        for (int i = 0; i < layoutSize; i++) {
+            Layout.Display displayLayout = layout.getAt(i);
+            if (displayLayout == null) {
+                continue;
+            }
+
+            // If the underlying display-device we want to use for this display
+            // doesn't exist, then skip it. This can happen at startup as display-devices
+            // trickle in one at a time. When the new display finally shows up, the layout is
+            // recalculated so that the display is properly added to the current layout.
+            final DisplayAddress address = displayLayout.getAddress();
+            final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
+            if (device == null) {
+                Slog.w(TAG, "The display device (" + address + "), is not available"
+                        + " for the display state " + deviceState);
+                continue;
+            }
+
+            // Find or create the LogicalDisplay to map the DisplayDevice to.
+            final int logicalDisplayId = displayLayout.getLogicalDisplayId();
+            final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
+            if (logicalDisplay == null) {
+                Slog.w(TAG, "The logical display (" + address + "), is not available"
+                        + " for the display state " + deviceState);
+                continue;
+            }
+            final DisplayInfo temp = logicalDisplay.getDisplayInfoLocked();
+            DisplayInfo displayInfo = new DisplayInfo(temp);
+            if (displayInfo.displayGroupId != groupId) {
+                // Ignore any displays not in the provided group.
+                continue;
+            }
+            // A display in the same group can be swapped out at any point, so set the display id
+            // for all results to the provided display id.
+            displayInfo.displayId = displayId;
+            displayInfos.add(displayInfo);
+        }
+        return displayInfos;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("LogicalDisplayMapper:");
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
@@ -261,6 +329,7 @@
 
         ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
         ipw.println("mCurrentLayout=" + mCurrentLayout);
+        ipw.println("mDeviceStateOnWhichToWakeUp=" + mDeviceStateOnWhichToWakeUp);
 
         final int logicalDisplayCount = mLogicalDisplays.size();
         ipw.println();
@@ -278,7 +347,9 @@
     }
 
     void setDeviceStateLocked(int state) {
-        Slog.i(TAG, "Requesting Transition to state: " + state);
+        final boolean isInteractive  = mPowerManager.isInteractive();
+        Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
+                + ", interactive=" + isInteractive);
         // As part of a state transition, we may need to turn off some displays temporarily so that
         // the transition is smooth. Plus, on some devices, only one internal displays can be
         // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
@@ -287,8 +358,13 @@
             resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
         }
         mPendingDeviceState = state;
-        if (areAllTransitioningDisplaysOffLocked()) {
-            // Nothing to wait on, we're good to go
+        final boolean wakeDevice = mPendingDeviceState == mDeviceStateOnWhichToWakeUp
+                && !isInteractive;
+
+        // If all displays are off already, we can just transition here, unless the device is asleep
+        // and we plan on waking it up. In that case, fall through to the call to wakeUp, and defer
+        // the final transition until later once the device is awake.
+        if (areAllTransitioningDisplaysOffLocked() && !wakeDevice) {
             transitionToPendingStateLocked();
             return;
         }
@@ -299,6 +375,14 @@
         // Send the transitioning phase updates to DisplayManager so that the displays can
         // start turning OFF in preparation for the new layout.
         updateLogicalDisplaysLocked();
+
+        if (wakeDevice) {
+            // We already told the displays to turn off, now we need to wake the device as
+            // we transition to this new state. We do it here so that the waking happens between the
+            // transition from one layout to another.
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNFOLD_DEVICE,
+                    "server.display:unfold");
+        }
         mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
                 TIMEOUT_STATE_TRANSITION_MILLIS);
     }
@@ -425,6 +509,7 @@
                 assignDisplayGroupLocked(display);
                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
 
+            // The display is involved in a display layout transition
             } else if (updateState == UPDATE_STATE_TRANSITION) {
                 mLogicalDisplaysToUpdate.put(displayId,
                         LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
@@ -615,14 +700,14 @@
 
             // We consider a display-device as changing/transition if
             // 1) It's already marked as transitioning
-            // 2) It's going from enabled to disabled
+            // 2) It's going from enabled to disabled, or vice versa
             // 3) It's enabled, but it's mapped to a new logical display ID. To the user this
             //    would look like apps moving from one screen to another since task-stacks stay
             //    with the logical display [ID].
             final boolean isTransitioning =
                     (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION)
-                    || (wasEnabled && !willBeEnabled)
-                    || (wasEnabled && deviceHasNewLogicalDisplayId);
+                    || (wasEnabled != willBeEnabled)
+                    || deviceHasNewLogicalDisplayId;
 
             if (isTransitioning) {
                 setDisplayPhase(logicalDisplay, phase);
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 2227506..4d9253e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -444,12 +444,11 @@
         capabilities = networkAttributes.mCapabilities;
         Log.i(TAG, String.format(
                 "updateNetworkState, state=%s, connected=%s, network=%s, capabilities=%s"
-                        + ", apn: %s, availableNetworkCount: %d",
+                        + ", availableNetworkCount: %d",
                 agpsDataConnStateAsString(),
                 isConnected,
                 network,
                 capabilities,
-                apn,
                 mAvailableNetworkAttributes.size()));
 
         if (native_is_agps_ril_supported()) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 62d8c32..345dc21 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -243,15 +243,24 @@
                 intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
             }
 
+            // send() SHOULD only run the completion callback if it completes successfully. however,
+            // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+            // for send() to throw an exception AND run the completion callback. if this happens, we
+            // would over-release the wakelock... we take matters into our own hands to ensure that
+            // the completion callback can only be run if send() completes successfully. this means
+            // the completion callback may be run inline - but as we've never specified what thread
+            // the callback is run on, this is fine.
+            GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+
             mPendingIntent.send(
                     mContext,
                     0,
                     intent,
-                    onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
-                            : null,
+                    (pI, i, rC, rD, rE) -> gatedCallback.run(),
                     null,
                     null,
                     options.toBundle());
+            gatedCallback.allow();
         }
 
         @Override
@@ -1960,11 +1969,6 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        if (mDelayedRegister != null) {
-            mAlarmHelper.cancel(mDelayedRegister);
-            mDelayedRegister = null;
-        }
-
         // calculate how long the new request should be delayed before sending it off to the
         // provider, under the assumption that once we send the request off, the provider will
         // immediately attempt to deliver a new location satisfying that request.
@@ -1997,8 +2001,8 @@
                 public void onAlarm() {
                     synchronized (mLock) {
                         if (mDelayedRegister == this) {
-                            setProviderRequest(newRequest);
                             mDelayedRegister = null;
+                            setProviderRequest(newRequest);
                         }
                     }
                 }
@@ -2025,6 +2029,11 @@
 
     @GuardedBy("mLock")
     void setProviderRequest(ProviderRequest request) {
+        if (mDelayedRegister != null) {
+            mAlarmHelper.cancel(mDelayedRegister);
+            mDelayedRegister = null;
+        }
+
         EVENT_LOG.logProviderUpdateRequest(mName, request);
         mProvider.getController().setRequest(request);
 
@@ -2734,4 +2743,49 @@
             }
         }
     }
+
+    private static class GatedCallback implements Runnable {
+
+        private @Nullable Runnable mCallback;
+
+        @GuardedBy("this")
+        private boolean mGate;
+        @GuardedBy("this")
+        private boolean mRun;
+
+        GatedCallback(Runnable callback) {
+            mCallback = callback;
+        }
+
+        public void allow() {
+            Runnable callback = null;
+            synchronized (this) {
+                mGate = true;
+                if (mRun && mCallback != null) {
+                    callback = mCallback;
+                    mCallback = null;
+                }
+            }
+
+            if (callback != null) {
+                callback.run();
+            }
+        }
+
+        @Override
+        public void run() {
+            Runnable callback = null;
+            synchronized (this) {
+                mRun = true;
+                if (mGate && mCallback != null) {
+                    callback = mCallback;
+                    mCallback = null;
+                }
+            }
+
+            if (callback != null) {
+                callback.run();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 22a675a..ad87c4530 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -105,20 +105,15 @@
 
         synchronized (mLock) {
             mDeviceIdleHelper.addListener(this);
-            mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
-            mDeviceStationaryHelper.addListener(this);
-            mDeviceStationary = false;
-            mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
-
-            onThrottlingChangedLocked(false);
+            onDeviceIdleChanged(mDeviceIdleHelper.isDeviceIdle());
         }
     }
 
     @Override
     protected void onStop() {
         synchronized (mLock) {
-            mDeviceStationaryHelper.removeListener(this);
             mDeviceIdleHelper.removeListener(this);
+            onDeviceIdleChanged(false);
 
             mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
             mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
@@ -151,13 +146,26 @@
             }
 
             mDeviceIdle = deviceIdle;
-            onThrottlingChangedLocked(false);
+
+            if (deviceIdle) {
+                // device stationary helper will deliver an immediate listener update
+                mDeviceStationaryHelper.addListener(this);
+            } else {
+                mDeviceStationaryHelper.removeListener(this);
+                mDeviceStationary = false;
+                mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+            }
         }
     }
 
     @Override
     public void onDeviceStationaryChanged(boolean deviceStationary) {
         synchronized (mLock) {
+            if (!mDeviceIdle) {
+                // stationary detection is only registered while idle - ignore late notifications
+                return;
+            }
+
             if (mDeviceStationary == deviceStationary) {
                 return;
             }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index bccc52f..ddaaa1e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1536,7 +1536,7 @@
                 @Override
                 public void onNullBinding(ComponentName name) {
                     Slog.v(TAG, "onNullBinding() called with: name = [" + name + "]");
-                    mServicesBound.remove(servicesBindingTag);
+                    mContext.unbindService(this);
                 }
             };
             if (!mContext.bindServiceAsUser(intent,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 78c909d..7ba0f04 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5397,6 +5397,7 @@
                                     == IMPORTANCE_NONE) {
                                 cancelNotificationsFromListener(token, new String[]{r.getKey()});
                             } else {
+                                r.setPendingLogUpdate(true);
                                 needsSort = true;
                             }
                         }
@@ -8057,64 +8058,151 @@
         }
     }
 
+    static class NotificationRecordExtractorData {
+        // Class that stores any field in a NotificationRecord that can change via an extractor.
+        // Used to cache previous data used in a sort.
+        int mPosition;
+        int mVisibility;
+        boolean mShowBadge;
+        boolean mAllowBubble;
+        boolean mIsBubble;
+        NotificationChannel mChannel;
+        String mGroupKey;
+        ArrayList<String> mOverridePeople;
+        ArrayList<SnoozeCriterion> mSnoozeCriteria;
+        Integer mUserSentiment;
+        Integer mSuppressVisually;
+        ArrayList<Notification.Action> mSystemSmartActions;
+        ArrayList<CharSequence> mSmartReplies;
+        int mImportance;
+
+        // These fields may not trigger a reranking but diffs here may be logged.
+        float mRankingScore;
+        boolean mIsConversation;
+
+        NotificationRecordExtractorData(int position, int visibility, boolean showBadge,
+                boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey,
+                ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria,
+                Integer userSentiment, Integer suppressVisually,
+                ArrayList<Notification.Action> systemSmartActions,
+                ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
+                boolean isConversation) {
+            mPosition = position;
+            mVisibility = visibility;
+            mShowBadge = showBadge;
+            mAllowBubble = allowBubble;
+            mIsBubble = isBubble;
+            mChannel = channel;
+            mGroupKey = groupKey;
+            mOverridePeople = overridePeople;
+            mSnoozeCriteria = snoozeCriteria;
+            mUserSentiment = userSentiment;
+            mSuppressVisually = suppressVisually;
+            mSystemSmartActions = systemSmartActions;
+            mSmartReplies = smartReplies;
+            mImportance = importance;
+            mRankingScore = rankingScore;
+            mIsConversation = isConversation;
+        }
+
+        // Returns whether the provided NotificationRecord differs from the cached data in any way.
+        // Should be guarded by mNotificationLock; not annotated here as this class is static.
+        boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) {
+            return mPosition != newPosition
+                    || mVisibility != r.getPackageVisibilityOverride()
+                    || mShowBadge != r.canShowBadge()
+                    || mAllowBubble != r.canBubble()
+                    || mIsBubble != r.getNotification().isBubbleNotification()
+                    || !Objects.equals(mChannel, r.getChannel())
+                    || !Objects.equals(mGroupKey, r.getGroupKey())
+                    || !Objects.equals(mOverridePeople, r.getPeopleOverride())
+                    || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
+                    || !Objects.equals(mUserSentiment, r.getUserSentiment())
+                    || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects())
+                    || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
+                    || !Objects.equals(mSmartReplies, r.getSmartReplies())
+                    || mImportance != r.getImportance();
+        }
+
+        // Returns whether the NotificationRecord has a change from this data for which we should
+        // log an update. This method specifically targets fields that may be changed via
+        // adjustments from the assistant.
+        //
+        // Fields here are the union of things in NotificationRecordLogger.shouldLogReported
+        // and NotificationRecord.applyAdjustments.
+        //
+        // Should be guarded by mNotificationLock; not annotated here as this class is static.
+        boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) {
+            return mPosition != newPosition
+                    || !Objects.equals(mChannel, r.getChannel())
+                    || !Objects.equals(mGroupKey, r.getGroupKey())
+                    || !Objects.equals(mOverridePeople, r.getPeopleOverride())
+                    || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
+                    || !Objects.equals(mUserSentiment, r.getUserSentiment())
+                    || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
+                    || !Objects.equals(mSmartReplies, r.getSmartReplies())
+                    || mImportance != r.getImportance()
+                    || !r.rankingScoreMatches(mRankingScore)
+                    || mIsConversation != r.isConversation();
+        }
+    }
+
     void handleRankingSort() {
         if (mRankingHelper == null) return;
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
             // Any field that can change via one of the extractors needs to be added here.
-            ArrayList<String> orderBefore = new ArrayList<>(N);
-            int[] visibilities = new int[N];
-            boolean[] showBadges = new boolean[N];
-            boolean[] allowBubbles = new boolean[N];
-            boolean[] isBubble = new boolean[N];
-            ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
-            ArrayList<String> groupKeyBefore = new ArrayList<>(N);
-            ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
-            ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
-            ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
-            ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
-            ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
-            ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
-            int[] importancesBefore = new int[N];
+            ArrayMap<String, NotificationRecordExtractorData> extractorDataBefore =
+                    new ArrayMap<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
-                orderBefore.add(r.getKey());
-                visibilities[i] = r.getPackageVisibilityOverride();
-                showBadges[i] = r.canShowBadge();
-                allowBubbles[i] = r.canBubble();
-                isBubble[i] = r.getNotification().isBubbleNotification();
-                channelBefore.add(r.getChannel());
-                groupKeyBefore.add(r.getGroupKey());
-                overridePeopleBefore.add(r.getPeopleOverride());
-                snoozeCriteriaBefore.add(r.getSnoozeCriteria());
-                userSentimentBefore.add(r.getUserSentiment());
-                suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
-                systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
-                smartRepliesBefore.add(r.getSmartReplies());
-                importancesBefore[i] = r.getImportance();
+                NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
+                        i,
+                        r.getPackageVisibilityOverride(),
+                        r.canShowBadge(),
+                        r.canBubble(),
+                        r.getNotification().isBubbleNotification(),
+                        r.getChannel(),
+                        r.getGroupKey(),
+                        r.getPeopleOverride(),
+                        r.getSnoozeCriteria(),
+                        r.getUserSentiment(),
+                        r.getSuppressedVisualEffects(),
+                        r.getSystemGeneratedSmartActions(),
+                        r.getSmartReplies(),
+                        r.getImportance(),
+                        r.getRankingScore(),
+                        r.isConversation());
+                extractorDataBefore.put(r.getKey(), extractorData);
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
-                if (!orderBefore.get(i).equals(r.getKey())
-                        || visibilities[i] != r.getPackageVisibilityOverride()
-                        || showBadges[i] != r.canShowBadge()
-                        || allowBubbles[i] != r.canBubble()
-                        || isBubble[i] != r.getNotification().isBubbleNotification()
-                        || !Objects.equals(channelBefore.get(i), r.getChannel())
-                        || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
-                        || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
-                        || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
-                        || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
-                        || !Objects.equals(suppressVisuallyBefore.get(i),
-                        r.getSuppressedVisualEffects())
-                        || !Objects.equals(systemSmartActionsBefore.get(i),
-                                r.getSystemGeneratedSmartActions())
-                        || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
-                        || importancesBefore[i] != r.getImportance()) {
+                if (!extractorDataBefore.containsKey(r.getKey())) {
+                    // This shouldn't happen given that we just built this with all the
+                    // notifications, but check just to be safe.
+                    continue;
+                }
+                if (extractorDataBefore.get(r.getKey()).hasDiffForRankingLocked(r, i)) {
                     mHandler.scheduleSendRankingUpdate();
-                    return;
+                }
+
+                // If this notification is one for which we wanted to log an update, and
+                // sufficient relevant bits are different, log update.
+                if (r.hasPendingLogUpdate()) {
+                    // We need to acquire the previous data associated with this specific
+                    // notification, as the one at the current index may be unrelated if
+                    // notification order has changed.
+                    NotificationRecordExtractorData prevData = extractorDataBefore.get(r.getKey());
+                    if (prevData.hasDiffForLoggingLocked(r, i)) {
+                        mNotificationRecordLogger.logNotificationAdjusted(r, i, 0,
+                                getGroupInstanceId(r.getSbn().getGroupKey()));
+                    }
+
+                    // Remove whether there was a diff or not; we've sorted the key, so if it
+                    // turns out there was nothing to log, that's fine too.
+                    r.setPendingLogUpdate(false);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f66cfa9..b4ca511 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -200,6 +200,10 @@
     private boolean mIsAppImportanceLocked;
     private ArraySet<Uri> mGrantableUris;
 
+    // Whether this notification record should have an update logged the next time notifications
+    // are sorted.
+    private boolean mPendingLogUpdate = false;
+
     public NotificationRecord(Context context, StatusBarNotification sbn,
             NotificationChannel channel) {
         this.sbn = sbn;
@@ -648,17 +652,23 @@
                     final ArrayList<String> people =
                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
                     setPeopleOverride(people);
+                    EventLogTags.writeNotificationAdjusted(
+                            getKey(), Adjustment.KEY_PEOPLE, people.toString());
                 }
                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
                             adjustment.getSignals().getParcelableArrayList(
                                     Adjustment.KEY_SNOOZE_CRITERIA);
                     setSnoozeCriteria(snoozeCriterionList);
+                    EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA,
+                            snoozeCriterionList.toString());
                 }
                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
                     final String groupOverrideKey =
                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
                     setOverrideGroupKey(groupOverrideKey);
+                    EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY,
+                            groupOverrideKey);
                 }
                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
                     // Only allow user sentiment update from assistant if user hasn't already
@@ -667,27 +677,42 @@
                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
                         setUserSentiment(adjustment.getSignals().getInt(
                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+                        EventLogTags.writeNotificationAdjusted(getKey(),
+                                Adjustment.KEY_USER_SENTIMENT,
+                                Integer.toString(getUserSentiment()));
                     }
                 }
                 if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
                     setSystemGeneratedSmartActions(
                             signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
+                    EventLogTags.writeNotificationAdjusted(getKey(),
+                            Adjustment.KEY_CONTEXTUAL_ACTIONS,
+                            getSystemGeneratedSmartActions().toString());
                 }
                 if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
+                    EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES,
+                            getSmartReplies().toString());
                 }
                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
                     importance = Math.min(IMPORTANCE_HIGH, importance);
                     setAssistantImportance(importance);
+                    EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE,
+                            Integer.toString(importance));
                 }
                 if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
                     mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
+                    EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE,
+                            Float.toString(mRankingScore));
                 }
                 if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
                     mIsNotConversationOverride = signals.getBoolean(
                             Adjustment.KEY_NOT_CONVERSATION);
+                    EventLogTags.writeNotificationAdjusted(getKey(),
+                            Adjustment.KEY_NOT_CONVERSATION,
+                            Boolean.toString(mIsNotConversationOverride));
                 }
                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
                     mAdjustmentIssuer = adjustment.getIssuer();
@@ -1478,6 +1503,24 @@
         return sbn;
     }
 
+    /**
+     * Returns whether this record's ranking score is approximately equal to otherScore
+     * (the difference must be within 0.0001).
+     */
+    public boolean rankingScoreMatches(float otherScore) {
+        return Math.abs(mRankingScore - otherScore) < 0.0001;
+    }
+
+    protected void setPendingLogUpdate(boolean pendingLogUpdate) {
+        mPendingLogUpdate = pendingLogUpdate;
+    }
+
+    // If a caller of this function subsequently logs the update, they should also call
+    // setPendingLogUpdate to false to make sure other callers don't also do so.
+    protected boolean hasPendingLogUpdate() {
+        return mPendingLogUpdate;
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff75..7d8564f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -59,6 +59,20 @@
             InstanceId groupId);
 
     /**
+     * Logs a NotificationReported atom reflecting an adjustment to a notification.
+     * Unlike maybeLogNotificationPosted, this method is guaranteed to log a notification update,
+     * so the caller must take responsibility for checking that that logging update is necessary,
+     * and that the notification is meaningfully changed.
+     * @param r The NotificationRecord. If null, no action is taken.
+     * @param position The position at which this notification is ranked.
+     * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
+     * @param groupId The instance Id of the group summary notification, or null.
+     */
+    void logNotificationAdjusted(@Nullable NotificationRecord r,
+            int position, int buzzBeepBlink,
+            InstanceId groupId);
+
+    /**
      * Logs a notification cancel / dismiss event using UiEventReported (event ids from the
      * NotificationCancelledEvents enum).
      * @param r The NotificationRecord. If null, no action is taken.
@@ -96,7 +110,9 @@
         @UiEvent(doc = "New notification enqueued to post")
         NOTIFICATION_POSTED(162),
         @UiEvent(doc = "Notification substantially updated, or alerted again.")
-        NOTIFICATION_UPDATED(163);
+        NOTIFICATION_UPDATED(163),
+        @UiEvent(doc = "Notification adjusted by assistant.")
+        NOTIFICATION_ADJUSTED(908);
 
         private final int mId;
         NotificationReportedEvent(int id) {
@@ -349,7 +365,8 @@
                     && Objects.equals(r.getSbn().getNotification().category,
                         old.getSbn().getNotification().category)
                     && (r.getImportance() == old.getImportance())
-                    && (getLoggingImportance(r) == getLoggingImportance(old)));
+                    && (getLoggingImportance(r) == getLoggingImportance(old))
+                    && r.rankingScoreMatches(old.getRankingScore()));
         }
 
         /**
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e..1859ec4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import android.annotation.Nullable;
+
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
@@ -37,33 +39,49 @@
         if (!p.shouldLogReported(buzzBeepBlink)) {
             return;
         }
+        writeNotificationReportedAtom(p, NotificationReportedEvent.fromRecordPair(p),
+                position, buzzBeepBlink, groupId);
+    }
+
+    @Override
+    public void logNotificationAdjusted(@Nullable NotificationRecord r,
+            int position, int buzzBeepBlink,
+            InstanceId groupId) {
+        NotificationRecordPair p = new NotificationRecordPair(r, null);
+        writeNotificationReportedAtom(p, NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+                position, buzzBeepBlink, groupId);
+    }
+
+    private void writeNotificationReportedAtom(NotificationRecordPair p,
+            NotificationReportedEvent eventType, int position, int buzzBeepBlink,
+            InstanceId groupId) {
         FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
-                /* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(),
-                /* int32 uid = 2 */ r.getUid(),
-                /* string package_name = 3 */ r.getSbn().getPackageName(),
+                /* int32 event_id = 1 */ eventType.getId(),
+                /* int32 uid = 2 */ p.r.getUid(),
+                /* string package_name = 3 */ p.r.getSbn().getPackageName(),
                 /* int32 instance_id = 4 */ p.getInstanceId(),
                 /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
                 /* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
                 /* string group_id_hash = 7 */ p.getGroupIdHash(),
                 /* int32 group_instance_id = 8 */ (groupId == null) ? 0 : groupId.getId(),
-                /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
-                /* string category = 10 */ r.getSbn().getNotification().category,
+                /* bool is_group_summary = 9 */ p.r.getSbn().getNotification().isGroupSummary(),
+                /* string category = 10 */ p.r.getSbn().getNotification().category,
                 /* int32 style = 11 */ p.getStyle(),
                 /* int32 num_people = 12 */ p.getNumPeople(),
                 /* int32 position = 13 */ position,
                 /* android.stats.sysui.NotificationImportance importance = 14 */
-                NotificationRecordLogger.getLoggingImportance(r),
+                NotificationRecordLogger.getLoggingImportance(p.r),
                 /* int32 alerting = 15 */ buzzBeepBlink,
                 /* NotificationImportanceExplanation importance_source = 16 */
-                r.getImportanceExplanationCode(),
+                p.r.getImportanceExplanationCode(),
                 /* android.stats.sysui.NotificationImportance importance_initial = 17 */
-                r.getInitialImportance(),
+                p.r.getInitialImportance(),
                 /* NotificationImportanceExplanation importance_initial_source = 18 */
-                r.getInitialImportanceExplanationCode(),
+                p.r.getInitialImportanceExplanationCode(),
                 /* android.stats.sysui.NotificationImportance importance_asst = 19 */
-                r.getAssistantImportance(),
+                p.r.getAssistantImportance(),
                 /* int32 assistant_hash = 20 */ p.getAssistantHash(),
-                /* float assistant_ranking_score = 21 */ r.getRankingScore()
+                /* float assistant_ranking_score = 21 */ p.r.getRankingScore()
         );
     }
 
diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
index 947405ed..b276c6f 100644
--- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
+++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
@@ -19,10 +19,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Build;
 import android.os.IDeviceIdentifiersPolicyService;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.server.SystemService;
@@ -65,11 +68,31 @@
         @Override
         public @Nullable String getSerialForPackage(String callingPackage,
                 String callingFeatureId) throws RemoteException {
+            if (!checkPackageBelongsToCaller(callingPackage)) {
+                throw new IllegalArgumentException(
+                        "Invalid callingPackage or callingPackage does not belong to caller's uid:"
+                                + Binder.getCallingUid());
+            }
+
             if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
                     callingPackage, callingFeatureId, "getSerial")) {
                 return Build.UNKNOWN;
             }
             return SystemProperties.get("ro.serialno", Build.UNKNOWN);
         }
+
+        private boolean checkPackageBelongsToCaller(String callingPackage) {
+            int callingUid = Binder.getCallingUid();
+            int callingUserId = UserHandle.getUserId(callingUid);
+            int callingPackageUid;
+            try {
+                callingPackageUid = mContext.getPackageManager().getPackageUidAsUser(
+                        callingPackage, callingUserId);
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+
+            return callingPackageUid == callingUid;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3691f60..1debaf3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -16163,8 +16163,7 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
-            boolean suspended) {
+    void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
         final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
         final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
         final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
@@ -16205,11 +16204,8 @@
             extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
             final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
                     ? null : allowListsToSend.get(i);
-            sendPackageBroadcast(
-                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
-                            : Intent.ACTION_PACKAGES_UNSUSPENDED,
-                    null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                    userIds, null, allowList, null);
+            sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
+                    null, userIds, null, allowList, null);
         }
     }
 
@@ -16523,6 +16519,8 @@
 
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
+        final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+        final IntArray modifiedUids = new IntArray(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
         final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
                 userId) : null;
@@ -16550,13 +16548,14 @@
                 unactionedPackages.add(packageName);
                 continue;
             }
-            boolean packageUnsuspended;
+            final boolean packageUnsuspended;
+            final boolean packageModified;
             synchronized (mLock) {
                 if (suspended) {
-                    pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
-                            launcherExtras, userId);
+                    packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+                            dialogInfo, appExtras, launcherExtras, userId);
                 } else {
-                    pkgSetting.removeSuspension(callingPackage, userId);
+                    packageModified = pkgSetting.removeSuspension(callingPackage, userId);
                 }
                 packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
             }
@@ -16564,18 +16563,29 @@
                 changedPackagesList.add(packageName);
                 changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
             }
+            if (packageModified) {
+                modifiedPackagesList.add(packageName);
+                modifiedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+            }
         }
 
         if (!changedPackagesList.isEmpty()) {
-            final String[] changedPackages = changedPackagesList.toArray(
-                    new String[changedPackagesList.size()]);
-            sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
+            final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+            sendPackagesSuspendedForUser(
+                    suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+                              : Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    changedPackages, changedUids.toArray(), userId);
             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
             synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
-        return unactionedPackages.toArray(new String[unactionedPackages.size()]);
+        // Send the suspension changed broadcast to ensure suspension state is not stale.
+        if (!modifiedPackagesList.isEmpty()) {
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                    modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+        }
+        return unactionedPackages.toArray(new String[0]);
     }
 
     @Override
@@ -16704,7 +16714,8 @@
             final String[] packageArray = unsuspendedPackages.toArray(
                     new String[unsuspendedPackages.size()]);
             sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
-            sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
+            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+                    packageArray, unsuspendedUids.toArray(), userId);
         }
     }
 
@@ -22646,8 +22657,9 @@
         removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
 
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+        StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         final int flags;
-        if (umInternal.isUserUnlockingOrUnlocked(userId)) {
+        if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         } else if (umInternal.isUserRunning(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE;
@@ -25512,9 +25524,11 @@
         // Reconcile app data for all started/unlocked users
         final StorageManager sm = mInjector.getSystemService(StorageManager.class);
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+        StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
             final int flags;
-            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+            if (StorageManager.isUserKeyUnlocked(user.id)
+                    && smInternal.isCeStoragePrepared(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
@@ -25854,7 +25868,8 @@
         StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
             final int flags;
-            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+            if (StorageManager.isUserKeyUnlocked(user.id)
+                    && smInternal.isCeStoragePrepared(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 88dd033..5536fc5 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -455,7 +455,7 @@
         return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
     }
 
-    void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
+    boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
             PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
         final PackageUserState existingUserState = modifyUserState(userId);
         final PackageUserState.SuspendParams newSuspendParams =
@@ -464,21 +464,27 @@
         if (existingUserState.suspendParams == null) {
             existingUserState.suspendParams = new ArrayMap<>();
         }
-        existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+        final PackageUserState.SuspendParams oldSuspendParams =
+                existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
         existingUserState.suspended = true;
         onChanged();
+        return !Objects.equals(oldSuspendParams, newSuspendParams);
     }
 
-    void removeSuspension(String suspendingPackage, int userId) {
+    boolean removeSuspension(String suspendingPackage, int userId) {
+        boolean wasModified = false;
         final PackageUserState existingUserState = modifyUserState(userId);
         if (existingUserState.suspendParams != null) {
-            existingUserState.suspendParams.remove(suspendingPackage);
+            if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
+                wasModified = true;
+            }
             if (existingUserState.suspendParams.size() == 0) {
                 existingUserState.suspendParams = null;
             }
         }
         existingUserState.suspended = (existingUserState.suspendParams != null);
         onChanged();
+        return wasModified;
     }
 
     void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d4feb3a..6d8137e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
 import android.os.UserManager.EnforcingUser;
 import android.os.UserManager.QuietModeFlag;
 import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
 import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
@@ -4815,6 +4816,10 @@
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
         mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+
+        StorageManagerInternal smInternal = LocalServices.getService(StorageManagerInternal.class);
+        smInternal.markCeStoragePrepared(userId);
+
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
     }
 
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 382398a..e0cc8e1 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -352,7 +352,7 @@
     }
 
     private void handleSafeModeStatusChanged() {
-        logDbg("VcnGatewayConnection safe mode status changed");
+        logVdbg("VcnGatewayConnection safe mode status changed");
         boolean hasSafeModeGatewayConnection = false;
 
         // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
@@ -368,7 +368,7 @@
                 hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
         if (oldStatus != mCurrentStatus) {
             mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
-            logDbg(
+            logInfo(
                     "Safe mode "
                             + (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited"));
         }
@@ -539,6 +539,16 @@
         Slog.d(TAG, getLogPrefix() + msg, tr);
     }
 
+    private void logInfo(String msg) {
+        Slog.i(TAG, getLogPrefix() + msg);
+        LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg);
+    }
+
+    private void logInfo(String msg, Throwable tr) {
+        Slog.i(TAG, getLogPrefix() + msg, tr);
+        LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr);
+    }
+
     private void logErr(String msg) {
         Slog.e(TAG, getLogPrefix() + msg);
         LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 450257f..7dec4e7 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1677,10 +1677,8 @@
             mFailedAttempts = 0;
             cancelSafeModeAlarm();
 
-            if (mIsInSafeMode) {
-                mIsInSafeMode = false;
-                mGatewayStatusCallback.onSafeModeStatusChanged();
-            }
+            mIsInSafeMode = false;
+            mGatewayStatusCallback.onSafeModeStatusChanged();
         }
 
         protected void applyTransform(
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 946fa44..19a9686 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -223,7 +223,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -1389,6 +1388,20 @@
         return parent != null ? parent.asTaskFragment() : null;
     }
 
+    /** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
+    private boolean shouldStartChangeTransition(
+            @Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
+        if (mWmService.mDisableTransitionAnimation
+                || mDisplayContent == null || newParent == null || oldParent == null
+                || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+            return false;
+        }
+
+        // Transition change for the activity moving into a TaskFragment of different bounds.
+        return newParent.isOrganizedTaskFragment()
+                && !newParent.getBounds().equals(oldParent.getBounds());
+    }
+
     @Override
     void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
         final TaskFragment oldParent = (TaskFragment) rawOldParent;
@@ -1397,6 +1410,10 @@
         final Task newTask = newParent != null ? newParent.getTask() : null;
         this.task = newTask;
 
+        if (shouldStartChangeTransition(newParent, oldParent)) {
+            initializeChangeTransition(getBounds());
+        }
+
         super.onParentChanged(newParent, oldParent);
 
         if (isPersistable()) {
@@ -2607,6 +2624,7 @@
         return parent != null ? parent.getOrganizedTaskFragment() : null;
     }
 
+    @Override
     boolean isEmbedded() {
         final TaskFragment parent = getTaskFragment();
         return parent != null && parent.isEmbedded();
@@ -3772,8 +3790,6 @@
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
 
-        commitVisibility(false /* visible */, true /* performLayout */);
-
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
         mWmService.mTaskSnapshotController.onAppRemoved(this);
@@ -3781,8 +3797,6 @@
         mTaskSupervisor.mStoppingActivities.remove(this);
         waitingToShow = false;
 
-        // TODO(b/169035022): move to a more-appropriate place.
-        mAtmService.getTransitionController().collect(this);
         // Defer removal of this activity when either a child is animating, or app transition is on
         // going. App transition animation might be applied on the parent task not on the activity,
         // but the actual frame buffer is associated with the activity, so we have to keep the
@@ -3798,6 +3812,16 @@
             delayed = true;
         }
 
+        // Don't commit visibility if it is waiting to animate. It will be set post animation.
+        if (!delayed) {
+            commitVisibility(false /* visible */, true /* performLayout */);
+        } else {
+            setVisibleRequested(false /* visible */);
+        }
+
+        // TODO(b/169035022): move to a more-appropriate place.
+        mAtmService.getTransitionController().collect(this);
+
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
                 getAnimation(),
@@ -4954,7 +4978,7 @@
     private void postApplyAnimation(boolean visible) {
         final boolean usingShellTransitions =
                 mAtmService.getTransitionController().getTransitionPlayer() != null;
-        final boolean delayed = isAnimating(PARENTS | CHILDREN,
+        final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
                 ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
                         | ANIMATION_TYPE_RECENTS);
         if (!delayed && !usingShellTransitions) {
@@ -5925,6 +5949,9 @@
     }
 
     void startFreezingScreen(int overrideOriginalDisplayRotation) {
+        if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+            return;
+        }
         ProtoLog.i(WM_DEBUG_ORIENTATION,
                 "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
                 appToken, isVisible(), mFreezingScreen, mVisibleRequested,
@@ -6891,7 +6918,7 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating(PARENTS,
+        final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS,
                 ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
 
         if (mSurfaceControl != null) {
@@ -8022,13 +8049,10 @@
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appRootTaskClipMode) {
-        if (appRootTaskClipMode == ROOT_TASK_CLIP_BEFORE_ANIM && getRootTask() != null) {
-            // Using the root task bounds here effectively applies the clipping before animation.
-            return getRootTask().getBounds();
-        }
-        // Use task-bounds if available so that activity-level letterbox (maxAspectRatio) is
+        // Use TaskFragment-bounds if available so that activity-level letterbox (maxAspectRatio) is
         // included in the animation.
-        return task != null ? task.getBounds() : getBounds();
+        final TaskFragment taskFragment = getTaskFragment();
+        return taskFragment != null ? taskFragment.getBounds() : getBounds();
     }
 
     @Override
@@ -8428,9 +8452,7 @@
         if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
             configChangeFlags |= changes;
-            if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) {
-                startFreezingScreenLocked(globalChanges);
-            }
+            startFreezingScreenLocked(globalChanges);
             forceNewConfig = false;
             // Do not preserve window if it is freezing screen because the original window won't be
             // able to update drawn state that causes freeze timeout.
@@ -9250,14 +9272,16 @@
                 task.getBounds(), Type.systemBars(), false /* ignoreVisibility */).toRect();
         InsetUtils.addInsets(insets, getLetterboxInsets());
 
-        return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
-                record.mAdapter.mCapturedLeash, !fillsParent(),
+        final RemoteAnimationTarget target = new RemoteAnimationTarget(task.mTaskId,
+                record.getMode(), record.mAdapter.mCapturedLeash, !fillsParent(),
                 new Rect(), insets,
                 getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
                 record.mAdapter.mRootTaskBounds, task.getWindowConfiguration(),
                 false /*isNotInRecents*/,
                 record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
                 record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
+        target.hasAnimatingParent = record.hasAnimatingParent();
+        return target;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 71ac730..980ebf0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.ACTIVITY_EMBEDDING;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.START_ABORTED;
@@ -1953,38 +1952,43 @@
             }
         }
 
-        if (mInTaskFragment != null && mInTaskFragment.getTask() != null) {
-            final int hostUid = mInTaskFragment.getTask().effectiveUid;
-            final int embeddingUid = targetTask != null ? targetTask.effectiveUid : r.getUid();
-            if (!canTaskBeEmbedded(hostUid, embeddingUid)) {
-                Slog.e(TAG, "Cannot embed activity to a task owned by " + hostUid + " targetTask= "
-                        + targetTask);
-                return START_PERMISSION_DENIED;
-            }
+        if (mInTaskFragment != null && !canEmbedActivity(mInTaskFragment, r, newTask, targetTask)) {
+            Slog.e(TAG, "Permission denied: Cannot embed " + r + " to " + mInTaskFragment.getTask()
+                    + " targetTask= " + targetTask);
+            return START_PERMISSION_DENIED;
         }
 
         return START_SUCCESS;
     }
 
     /**
-     * Return {@code true} if the {@param task} can embed another task.
-     * @param hostUid the uid of the host task
-     * @param embeddedUid the uid of the task the are going to be embedded
+     * Return {@code true} if an activity can be embedded to the TaskFragment.
+     * @param taskFragment the TaskFragment for embedding.
+     * @param starting the starting activity.
+     * @param newTask whether the starting activity is going to be launched on a new task.
+     * @param targetTask the target task for launching activity, which could be different from
+     *                   the one who hosting the embedding.
      */
-    private boolean canTaskBeEmbedded(int hostUid, int embeddedUid) {
+    private boolean canEmbedActivity(@NonNull TaskFragment taskFragment, ActivityRecord starting,
+            boolean newTask, Task targetTask) {
+        final Task hostTask = taskFragment.getTask();
+        if (hostTask == null) {
+            return false;
+        }
+
         // Allowing the embedding if the task is owned by system.
+        final int hostUid = hostTask.effectiveUid;
         if (hostUid == Process.SYSTEM_UID) {
             return true;
         }
 
-        // Allowing embedding if the host task is owned by an app that has the ACTIVITY_EMBEDDING
-        // permission
-        if (mService.checkPermission(ACTIVITY_EMBEDDING, -1, hostUid) == PERMISSION_GRANTED) {
-            return true;
+        // Not allowed embedding an activity of another app.
+        if (hostUid != starting.getUid()) {
+            return false;
         }
 
-        // Allowing embedding if it is from the same app that owned the task
-        return hostUid == embeddedUid;
+        // Not allowed embedding task.
+        return !newTask && (targetTask == null || targetTask == hostTask);
     }
 
     /**
@@ -2801,10 +2805,15 @@
                 newParent = mInTaskFragment;
             }
         } else {
-            // Use the child TaskFragment (if any) as the new parent if the activity can be embedded
             final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */,
                     false /* includingEmbeddedTask */);
-            newParent = top != null ? top.getTaskFragment() : task;
+            final TaskFragment taskFragment = top != null ? top.getTaskFragment() : null;
+            if (taskFragment != null && taskFragment.isEmbedded()
+                    && canEmbedActivity(taskFragment, mStartActivity, false /* newTask */, task)) {
+                // Use the embedded TaskFragment of the top activity as the new parent if the
+                // activity can be embedded.
+                newParent = top.getTaskFragment();
+            }
         }
 
         if (mStartActivity.getTaskFragment() == null
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1c8f6f1..681ff90 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6554,7 +6554,8 @@
 
         @Override
         public PackageConfigurationUpdater createPackageConfigurationUpdater() {
-            return new PackageConfigurationUpdaterImpl(Binder.getCallingPid());
+            return new PackageConfigurationUpdaterImpl(Binder.getCallingPid(),
+                    ActivityTaskManagerService.this);
         }
 
         @Override
@@ -6571,57 +6572,4 @@
                     null /* trigger */, mRootWindowContainer.getDefaultDisplay());
         }
     }
-
-    final class PackageConfigurationUpdaterImpl implements
-            ActivityTaskManagerInternal.PackageConfigurationUpdater {
-        private final int mPid;
-        private Integer mNightMode;
-        private LocaleList mLocales;
-
-        PackageConfigurationUpdaterImpl(int pid) {
-            mPid = pid;
-        }
-
-        @Override
-        public ActivityTaskManagerInternal.PackageConfigurationUpdater setNightMode(int nightMode) {
-            mNightMode = nightMode;
-            return this;
-        }
-
-        @Override
-        public ActivityTaskManagerInternal.PackageConfigurationUpdater
-                setLocales(LocaleList locales) {
-            mLocales = locales;
-            return this;
-        }
-
-        @Override
-        public void commit() {
-            synchronized (mGlobalLock) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    final WindowProcessController wpc = mProcessMap.getProcess(mPid);
-                    if (wpc == null) {
-                        Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
-                        return;
-                    }
-                    LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
-                            mLocales, getGlobalConfiguration().getLocales());
-                    wpc.applyAppSpecificConfig(mNightMode, localesOverride);
-                    wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
-                    mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-
-        Integer getNightMode() {
-            return mNightMode;
-        }
-
-        LocaleList getLocales() {
-            return mLocales;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e6aa4fc..0497fb5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1940,6 +1940,7 @@
     void removeHistoryRecords(WindowProcessController app) {
         removeHistoryRecords(mStoppingActivities, app, "mStoppingActivities");
         removeHistoryRecords(mFinishingActivities, app, "mFinishingActivities");
+        removeHistoryRecords(mNoHistoryActivities, app, "mNoHistoryActivities");
     }
 
     private void removeHistoryRecords(ArrayList<ActivityRecord> list, WindowProcessController app,
@@ -1977,6 +1978,7 @@
                 mWaitingActivityLaunched.get(i).dump(pw, prefix + "  ");
             }
         }
+        pw.println(prefix + "mNoHistoryActivities=" + mNoHistoryActivities);
         pw.println();
     }
 
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 529c4f6..486328a 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.annotation.ColorInt;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
@@ -106,4 +107,14 @@
     default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
         return false;
     }
+
+    /**
+     * Gets the background color to show behind an animation.
+     *
+     * @return The background color to show behind an animation (0 for no background color).
+     */
+    @ColorInt
+    default int getBackgroundColor() {
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 7a42351..82377b3 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -165,8 +165,8 @@
     void handleAppTransitionReady() {
         mTempTransitionReasons.clear();
         if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
-                || !transitionGoodToGo(mDisplayContent.mChangingContainers,
-                        mTempTransitionReasons)) {
+                || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
+                || !transitionGoodToGoForTaskFragments()) {
             return;
         }
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -237,6 +237,8 @@
 
         // Check if there is any override
         if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+            // Unfreeze the windows that were previously frozen for TaskFragment animation.
+            unfreezeEmbeddedChangingWindows();
             overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
         }
 
@@ -341,6 +343,9 @@
             switch (changingType) {
                 case TYPE_TASK:
                     return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+                case TYPE_ACTIVITY:
+                    // ActivityRecord is put in a change transition only when it is reparented
+                    // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition.
                 case TYPE_TASK_FRAGMENT:
                     return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
                 default:
@@ -511,6 +516,16 @@
                 : null;
     }
 
+    private void unfreezeEmbeddedChangingWindows() {
+        final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers;
+        for (int i = changingContainers.size() - 1; i >= 0; i--) {
+            final WindowContainer wc = changingContainers.valueAt(i);
+            if (wc.isEmbedded()) {
+                wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction());
+            }
+        }
+    }
+
     /**
      * Overrides the pending transition with the remote animation defined by the
      * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
@@ -584,6 +599,13 @@
         }
     }
 
+    @Nullable
+    static Task findRootTaskFromContainer(WindowContainer wc) {
+        return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask()
+                : wc.asActivityRecord().getRootTask();
+    }
+
+    @Nullable
     static ActivityRecord getAppFromContainer(WindowContainer wc) {
         return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
                 : wc.asActivityRecord();
@@ -957,72 +979,109 @@
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
                 mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
-
+        if (mDisplayContent.mAppTransition.isTimeout()) {
+            return true;
+        }
         final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
                 Display.DEFAULT_DISPLAY).getRotationAnimation();
 
-        if (!mDisplayContent.mAppTransition.isTimeout()) {
-            // Imagine the case where we are changing orientation due to an app transition, but a
-            // previous orientation change is still in progress. We won't process the orientation
-            // change for our transition because we need to wait for the rotation animation to
-            // finish.
-            // If we start the app transition at this point, we will interrupt it halfway with a
-            // new rotation animation after the old one finally finishes. It's better to defer the
-            // app transition.
-            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
-                    && mDisplayContent.getDisplayRotation().needsUpdate()) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Delaying app transition for screen rotation animation to finish");
-                return false;
-            }
-            for (int i = 0; i < apps.size(); i++) {
-                WindowContainer wc = apps.valueAt(i);
-                final ActivityRecord activity = getAppFromContainer(wc);
-                if (activity == null) {
-                    continue;
-                }
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
-                                + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
-                        activity, activity.allDrawn, activity.startingDisplayed,
-                        activity.startingMoved, activity.isRelaunching(),
-                        activity.mStartingWindow);
-
-
-                final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
-                if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
-                    return false;
-                }
-                if (allDrawn) {
-                    outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
-                } else {
-                    outReasons.put(activity,
-                            activity.mStartingData instanceof SplashScreenStartingData
-                                    ? APP_TRANSITION_SPLASH_SCREEN
-                                    : APP_TRANSITION_SNAPSHOT);
-                }
-            }
-
-            // We also need to wait for the specs to be fetched, if needed.
-            if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
-                return false;
-            }
-
-            if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
-                            mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
-                return false;
-            }
-
-            // If the wallpaper is visible, we need to check it's ready too.
-            boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
-                    mWallpaperControllerLocked.wallpaperTransitionReady();
-            if (wallpaperReady) {
-                return true;
-            }
+        // Imagine the case where we are changing orientation due to an app transition, but a
+        // previous orientation change is still in progress. We won't process the orientation
+        // change for our transition because we need to wait for the rotation animation to
+        // finish.
+        // If we start the app transition at this point, we will interrupt it halfway with a
+        // new rotation animation after the old one finally finishes. It's better to defer the
+        // app transition.
+        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
+                && mDisplayContent.getDisplayRotation().needsUpdate()) {
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                    "Delaying app transition for screen rotation animation to finish");
             return false;
         }
+        for (int i = 0; i < apps.size(); i++) {
+            WindowContainer wc = apps.valueAt(i);
+            final ActivityRecord activity = getAppFromContainer(wc);
+            if (activity == null) {
+                continue;
+            }
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                    "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+                            + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+                    activity, activity.allDrawn, activity.startingDisplayed,
+                    activity.startingMoved, activity.isRelaunching(),
+                    activity.mStartingWindow);
+
+            final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
+            if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
+                return false;
+            }
+            if (allDrawn) {
+                outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
+            } else {
+                outReasons.put(activity,
+                        activity.mStartingData instanceof SplashScreenStartingData
+                                ? APP_TRANSITION_SPLASH_SCREEN
+                                : APP_TRANSITION_SNAPSHOT);
+            }
+        }
+
+        // We also need to wait for the specs to be fetched, if needed.
+        if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
+            return false;
+        }
+
+        if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
+            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
+                    mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
+            return false;
+        }
+
+        // If the wallpaper is visible, we need to check it's ready too.
+        return !mWallpaperControllerLocked.isWallpaperVisible()
+                || mWallpaperControllerLocked.wallpaperTransitionReady();
+    }
+
+    private boolean transitionGoodToGoForTaskFragments() {
+        if (mDisplayContent.mAppTransition.isTimeout()) {
+            return true;
+        }
+
+        // Check all Tasks in this transition. This is needed because new TaskFragment created for
+        // launching activity may not be in the tracking lists, but we still want to wait for the
+        // activity launch to start the transition.
+        final ArraySet<Task> rootTasks = new ArraySet<>();
+        for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
+            rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask());
+        }
+        for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
+            rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask());
+        }
+        for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
+            rootTasks.add(
+                    findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i)));
+        }
+
+        // Organized TaskFragment can be empty for two situations:
+        // 1. New created and is waiting for Activity launch. In this case, we want to wait for
+        //    the Activity launch to trigger the transition.
+        // 2. Last Activity is just removed. In this case, we want to wait for organizer to
+        //    remove the TaskFragment because it may also want to change other TaskFragments in
+        //    the same transition.
+        for (int i = rootTasks.size() - 1; i >= 0; i--) {
+            final Task rootTask = rootTasks.valueAt(i);
+            final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
+                if (!taskFragment.isReadyToTransit()) {
+                    ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
+                            taskFragment);
+                    return true;
+                }
+                return false;
+            });
+            if (notReady) {
+                return false;
+            }
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 63f6387..f800f0e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -76,6 +76,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
@@ -1389,11 +1390,16 @@
         final Configuration currentDisplayConfig = getConfiguration();
         mTmpConfiguration.setTo(currentDisplayConfig);
         computeScreenConfiguration(mTmpConfiguration);
-        configChanged |= currentDisplayConfig.diff(mTmpConfiguration) != 0;
+        final int changes = currentDisplayConfig.diff(mTmpConfiguration);
+        configChanged |= changes != 0;
 
         if (configChanged) {
             mWaitingForConfig = true;
-            mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
+            if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+                requestChangeTransitionIfNeeded(changes);
+            } else {
+                mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
+            }
             sendNewConfiguration();
         }
 
@@ -3165,6 +3171,24 @@
         return mScreenRotationAnimation;
     }
 
+    /**
+     * Requests to start a transition for the display configuration change. The given changes must
+     * be non-zero. This method is no-op if the display has been collected.
+     */
+    void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) {
+        final TransitionController controller = mAtmService.getTransitionController();
+        if (controller.isCollecting()) {
+            if (!controller.isCollecting(this)) {
+                controller.collect(this);
+            }
+            return;
+        }
+        final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this);
+        if (t != null) {
+            t.setKnownConfigChanges(this, changes);
+        }
+    }
+
     /** If the display is in transition, there should be a screenshot covering it. */
     @Override
     boolean inTransition() {
@@ -5724,6 +5748,9 @@
             }
             mWmService.mDisplayNotificationController.dispatchDisplayChanged(
                     this, getConfiguration());
+            if (isReady() && mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+                requestChangeTransitionIfNeeded(changes);
+            }
         }
         return changes;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 73d6cec..971bebd 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
-import static android.view.WindowManager.TRANSIT_CHANGE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
@@ -444,7 +443,9 @@
             }
 
             if (mDisplayContent.mFixedRotationTransitionListener
-                    .isTopFixedOrientationRecentsAnimating()) {
+                    .isTopFixedOrientationRecentsAnimating()
+                    // If screen is off or the device is going to sleep, then still allow to update.
+                    && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
                 // During the recents animation, the closing app might still be considered on top.
                 // In order to ignore its requested orientation to avoid a sensor led rotation (e.g
                 // user rotating the device while the recents animation is running), we ignore
@@ -490,12 +491,6 @@
             recentsAnimationController.cancelAnimationForDisplayChange();
         }
 
-        final Transition t = (useShellTransitions
-                && !mService.mAtmService.getTransitionController().isCollecting())
-                ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE)
-                : null;
-        mService.mAtmService.getTransitionController().collect(mDisplayContent);
-
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
                         displayId, rotation, oldRotation, lastOrientation);
@@ -509,11 +504,10 @@
         mDisplayContent.setLayoutNeeded();
 
         if (useShellTransitions) {
-            if (t != null) {
-                // This created its own transition, so send a start request.
-                mService.mAtmService.getTransitionController().requestStartTransition(
-                        t, null /* trigger */, null /* remote */);
-            } else {
+            final boolean wasInTransition = mDisplayContent.inTransition();
+            mDisplayContent.requestChangeTransitionIfNeeded(
+                    ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
+            if (wasInTransition) {
                 // Use remote-rotation infra since the transition has already been requested
                 // TODO(shell-transitions): Remove this once lifecycle management can cover all
                 //                          rotation cases.
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index d12d07a..cc6a880 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.content.ClipData;
+import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -32,6 +33,7 @@
 import android.view.IWindow;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
 
@@ -43,7 +45,8 @@
  */
 class DragDropController {
     private static final float DRAG_SHADOW_ALPHA_TRANSPARENT = .7071f;
-    private static final long DRAG_TIMEOUT_MS = 5000;
+    static final long DRAG_TIMEOUT_MS = 5000;
+    private static final int A11Y_DRAG_TIMEOUT_DEFAULT_MS = 60000;
 
     // Messages for Handler.
     static final int MSG_DRAG_END_TIMEOUT = 0;
@@ -151,36 +154,48 @@
                     mDragState.mOriginalAlpha = alpha;
                     mDragState.mToken = dragToken;
                     mDragState.mDisplayContent = displayContent;
-
-                    final Display display = displayContent.getDisplay();
-                    if (!mCallback.get().registerInputChannel(
-                            mDragState, display, mService.mInputManager,
-                            callingWin.mInputChannel)) {
-                        Slog.e(TAG_WM, "Unable to transfer touch focus");
-                        return null;
-                    }
-
-                    final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
                     mDragState.mData = data;
-                    mDragState.broadcastDragStartedLocked(touchX, touchY);
-                    mDragState.overridePointerIconLocked(touchSource);
-                    // remember the thumb offsets for later
-                    mDragState.mThumbOffsetX = thumbCenterX;
-                    mDragState.mThumbOffsetY = thumbCenterY;
 
-                    // Make the surface visible at the proper location
-                    if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
+                    if ((flags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) == 0) {
+                        final Display display = displayContent.getDisplay();
+                        if (!mCallback.get().registerInputChannel(
+                                mDragState, display, mService.mInputManager,
+                                callingWin.mInputChannel)) {
+                            Slog.e(TAG_WM, "Unable to transfer touch focus");
+                            return null;
+                        }
 
-                    final SurfaceControl.Transaction transaction = mDragState.mTransaction;
-                    transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
-                    transaction.setPosition(
-                            surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
-                    transaction.show(surfaceControl);
-                    displayContent.reparentToOverlay(transaction, surfaceControl);
-                    callingWin.scheduleAnimation();
+                        final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
+                        mDragState.broadcastDragStartedLocked(touchX, touchY);
+                        mDragState.overridePointerIconLocked(touchSource);
+                        // remember the thumb offsets for later
+                        mDragState.mThumbOffsetX = thumbCenterX;
+                        mDragState.mThumbOffsetY = thumbCenterY;
 
-                    if (SHOW_LIGHT_TRANSACTIONS) {
-                        Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+                        // Make the surface visible at the proper location
+                        if (SHOW_LIGHT_TRANSACTIONS) {
+                            Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
+                        }
+
+                        final SurfaceControl.Transaction transaction = mDragState.mTransaction;
+                        transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
+                        transaction.setPosition(
+                                surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
+                        transaction.show(surfaceControl);
+                        displayContent.reparentToOverlay(transaction, surfaceControl);
+                        callingWin.scheduleAnimation();
+                        if (SHOW_LIGHT_TRANSACTIONS) {
+                            Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+                        }
+                    } else {
+                        // Skip surface logic for a drag triggered by an AccessibilityAction
+                        mDragState.broadcastDragStartedLocked(touchX, touchY);
+
+                        // Timeout for the user to drop the content
+                        sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, callingWin.mClient.asBinder(),
+                                getAccessibilityManager().getRecommendedTimeoutMillis(
+                                        A11Y_DRAG_TIMEOUT_DEFAULT_MS,
+                                        AccessibilityManager.FLAG_CONTENT_CONTROLS));
                     }
                 } finally {
                     if (surface != null) {
@@ -309,10 +324,10 @@
     /**
      * Sends a timeout message to the Handler managed by DragDropController.
      */
-    void sendTimeoutMessage(int what, Object arg) {
+    void sendTimeoutMessage(int what, Object arg, long timeoutMs) {
         mHandler.removeMessages(what, arg);
         final Message msg = mHandler.obtainMessage(what, arg);
-        mHandler.sendMessageDelayed(msg, DRAG_TIMEOUT_MS);
+        mHandler.sendMessageDelayed(msg, timeoutMs);
     }
 
     /**
@@ -332,6 +347,30 @@
         }
     }
 
+    boolean dropForAccessibility(IWindow window, float x, float y) {
+        synchronized (mService.mGlobalLock) {
+            final boolean isA11yEnabled = getAccessibilityManager().isEnabled();
+            if (!dragDropActiveLocked()) {
+                return false;
+            }
+            if (mDragState.isAccessibilityDragDrop() && isA11yEnabled) {
+                final WindowState winState = mService.windowForClientLocked(
+                        null, window, false);
+                if (!mDragState.isWindowNotified(winState)) {
+                    return false;
+                }
+                IBinder token = winState.mInputChannelToken;
+                return mDragState.reportDropWindowLock(token, x, y);
+            }
+            return false;
+        }
+    }
+
+    AccessibilityManager getAccessibilityManager() {
+        return (AccessibilityManager) mService.mContext.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+    }
+
     private class DragHandler extends Handler {
         /**
          * Lock for window manager.
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index aa257f8..4fc123d 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -253,7 +253,7 @@
                 mTransaction.reparent(mSurfaceControl, null).apply();
             } else {
                 mDragDropController.sendTimeoutMessage(MSG_REMOVE_DRAG_SURFACE_TIMEOUT,
-                        mSurfaceControl);
+                        mSurfaceControl, DragDropController.DRAG_TIMEOUT_MS);
             }
             mSurfaceControl = null;
         }
@@ -276,9 +276,9 @@
      * Notify the drop target and tells it about the data. If the drop event is not sent to the
      * target, invokes {@code endDragLocked} immediately.
      */
-    void reportDropWindowLock(IBinder token, float x, float y) {
+    boolean reportDropWindowLock(IBinder token, float x, float y) {
         if (mAnimator != null) {
-            return;
+            return false;
         }
 
         final WindowState touchedWin = mService.mInputToWindowMap.get(token);
@@ -288,7 +288,7 @@
             mDragResult = false;
             endDragLocked();
             if (DEBUG_DRAG) Slog.d(TAG_WM, "Drop outside a valid window " + touchedWin);
-            return;
+            return false;
         }
 
         if (DEBUG_DRAG) Slog.d(TAG_WM, "sending DROP to " + touchedWin);
@@ -322,16 +322,19 @@
             touchedWin.mClient.dispatchDragEvent(event);
 
             // 5 second timeout for this window to respond to the drop
-            mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, clientToken);
+            mDragDropController.sendTimeoutMessage(MSG_DRAG_END_TIMEOUT, clientToken,
+                    DragDropController.DRAG_TIMEOUT_MS);
         } catch (RemoteException e) {
             Slog.w(TAG_WM, "can't send drop notification to win " + touchedWin);
             endDragLocked();
+            return false;
         } finally {
             if (myPid != touchedWin.mSession.mPid) {
                 event.recycle();
             }
         }
         mToken = clientToken;
+        return true;
     }
 
     class InputInterceptor {
@@ -553,7 +556,7 @@
         }
     }
 
-    private boolean isWindowNotified(WindowState newWin) {
+    boolean isWindowNotified(WindowState newWin) {
         for (WindowState ws : mNotifiedWindows) {
             if (ws == newWin) {
                 return true;
@@ -567,8 +570,10 @@
             return;
         }
         if (!mDragResult) {
-            mAnimator = createReturnAnimationLocked();
-            return;  // Will call closeLocked() when the animation is done.
+            if (!isAccessibilityDragDrop()) {
+                mAnimator = createReturnAnimationLocked();
+                return;  // Will call closeLocked() when the animation is done.
+            }
         }
         closeLocked();
     }
@@ -577,7 +582,7 @@
         if (mAnimator != null) {
             return;
         }
-        if (!mDragInProgress || skipAnimation) {
+        if (!mDragInProgress || skipAnimation || isAccessibilityDragDrop()) {
             // mDragInProgress is false if an app invokes Session#cancelDragAndDrop before
             // Session#performDrag. Reset the drag state without playing the cancel animation
             // because H.DRAG_START_TIMEOUT may be sent to WindowManagerService, which will cause
@@ -722,4 +727,8 @@
             mDragDropController.sendHandlerMessage(MSG_ANIMATION_END, null);
         }
     }
+
+    boolean isAccessibilityDragDrop() {
+        return (mFlags & View.DRAG_FLAG_ACCESSIBILITY_ACTION) != 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index fed4f62..6560d15 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -103,12 +103,12 @@
         ArrayList<TaskFragment> adjacentTaskFragments = null;
         for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer child = mTaskFragment.mChildren.get(i);
-            if (child.asTaskFragment() != null) {
-                final TaskFragment childTaskFragment = child.asTaskFragment();
+            final TaskFragment childTaskFragment = child.asTaskFragment();
+            if (childTaskFragment != null && childTaskFragment.topRunningActivity() != null) {
                 childTaskFragment.updateActivityVisibilities(starting, configChanges,
                         preserveWindows, notifyClients);
-                mBehindFullyOccludedContainer |= childTaskFragment.getBounds().equals(
-                        mTaskFragment.getBounds());
+                mBehindFullyOccludedContainer |=
+                        childTaskFragment.getBounds().equals(mTaskFragment.getBounds());
                 if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
                     mAboveTop = false;
                 }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 767e2c2..403df91 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -163,6 +163,7 @@
             // animate-out as new one animates-in.
             mWin.cancelAnimation();
             mWin.mProvidedInsetsSources.remove(mSource.getType());
+            mSeamlessRotating = false;
         }
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", win,
                 InsetsState.typeToString(mSource.getType()));
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index c18c94d..45411a9 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -247,7 +247,8 @@
         TapEventReceiver(InputChannel inputChannel, Context context) {
             super(inputChannel, UiThread.getHandler().getLooper());
             mDoubleTapListener = new DoubleTapListener();
-            mDoubleTapDetector = new GestureDetector(context, mDoubleTapListener);
+            mDoubleTapDetector = new GestureDetector(
+                    context, mDoubleTapListener, UiThread.getHandler());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 76a098d..72fbfcc 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -174,7 +174,7 @@
      * Whether corners of letterboxed activities are rounded.
      */
     boolean isLetterboxActivityCornersRounded() {
-        return getLetterboxActivityCornersRadius() > 0;
+        return getLetterboxActivityCornersRadius() != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 0d99bac..cf2afc9 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -38,6 +38,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Slog;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
@@ -295,31 +298,66 @@
     }
 
     private void updateRoundedCorners(WindowState mainWindow) {
-        int cornersRadius =
-                // Don't round corners if letterboxed only for display cutout.
-                shouldShowLetterboxUi(mainWindow)
-                                && !mainWindow.isLetterboxedForDisplayCutout()
-                        ? Math.max(0, mLetterboxConfiguration.getLetterboxActivityCornersRadius())
-                        : 0;
-        setCornersRadius(mainWindow, cornersRadius);
-    }
-
-    private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
         final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
         if (windowSurface != null && windowSurface.isValid()) {
             Transaction transaction = mActivityRecord.getSyncTransaction();
-            transaction.setCornerRadius(windowSurface, cornersRadius);
+
+            if (!isLetterboxedNotForDisplayCutout(mainWindow)
+                    || !mLetterboxConfiguration.isLetterboxActivityCornersRounded()) {
+                transaction
+                        .setWindowCrop(windowSurface, null)
+                        .setCornerRadius(windowSurface, 0);
+                return;
+            }
+
+            final InsetsState insetsState = mainWindow.getInsetsState();
+            final InsetsSource taskbarInsetsSource =
+                    insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+
+            Rect cropBounds = new Rect(mActivityRecord.getBounds());
+            // Activity bounds are in screen coordinates while (0,0) for activity's surface control
+            // is at the top left corner of an app window so offsetting bounds accordingly.
+            cropBounds.offsetTo(0, 0);
+            // Rounded cornerners should be displayed above the taskbar.
+            cropBounds.bottom = Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
+            transaction
+                    .setWindowCrop(windowSurface, cropBounds)
+                    .setCornerRadius(windowSurface, getRoundedCorners(insetsState));
         }
     }
 
+    // Returns rounded corners radius based on override in
+    // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+    // Device corners can be different on the right and left sides but we use the same radius
+    // for all corners for consistency and pick a minimal bottom one for consistency with a
+    // taskbar rounded corners.
+    private int getRoundedCorners(InsetsState insetsState) {
+        if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
+            return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
+        }
+        return Math.min(
+                getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+                getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+    }
+
+    private int getInsetsStateCornerRadius(
+                InsetsState insetsState, @RoundedCorner.Position int position) {
+        RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+        return corner == null ? 0 : corner.getRadius();
+    }
+
+    private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
+        return shouldShowLetterboxUi(mainWindow)
+                && !mainWindow.isLetterboxedForDisplayCutout();
+    }
+
     private void updateWallpaperForLetterbox(WindowState mainWindow) {
         @LetterboxBackgroundType int letterboxBackgroundType =
                 mLetterboxConfiguration.getLetterboxBackgroundType();
         boolean wallpaperShouldBeShown =
                 letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
-                        && shouldShowLetterboxUi(mainWindow)
                         // Don't use wallpaper as a background if letterboxed for display cutout.
-                        && !mainWindow.isLetterboxedForDisplayCutout()
+                        && isLetterboxedNotForDisplayCutout(mainWindow)
                         // Check that dark scrim alpha or blur radius are provided
                         && (getLetterboxWallpaperBlurRadius() > 0
                                 || getLetterboxWallpaperDarkScrimAlpha() > 0)
@@ -375,6 +413,8 @@
         pw.println(prefix + "  letterboxBackgroundType="
                 + letterboxBackgroundTypeToString(
                         mLetterboxConfiguration.getLetterboxBackgroundType()));
+        pw.println(prefix + "  letterboxCornerRadius="
+                + getRoundedCorners(mainWin.getInsetsState()));
         if (mLetterboxConfiguration.getLetterboxBackgroundType()
                 == LETTERBOX_BACKGROUND_WALLPAPER) {
             pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 520bd8b..8f161bf 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -19,6 +19,7 @@
 import static com.android.server.wm.AnimationAdapterProto.LOCAL;
 import static com.android.server.wm.LocalAnimationAdapterProto.ANIMATION_SPEC;
 
+import android.annotation.ColorInt;
 import android.os.SystemClock;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
@@ -72,6 +73,12 @@
     }
 
     @Override
+    @ColorInt
+    public int getBackgroundColor() {
+        return mSpec.getBackgroundColor();
+    }
+
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         mSpec.dump(pw, prefix);
     }
@@ -149,5 +156,9 @@
         }
 
         void dumpDebugInner(ProtoOutputStream proto);
+
+        default int getBackgroundColor() {
+            return 0;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 505c4beb..52eea4d 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -173,7 +173,7 @@
 
     @GuardedBy("mLock")
     void updateFromImpl(String packageName, int userId,
-            ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) {
+            PackageConfigurationUpdaterImpl impl) {
         synchronized (mLock) {
             PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
             if (impl.getNightMode() != null) {
diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
new file mode 100644
index 0000000..96025054
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Binder;
+import android.os.LocaleList;
+import android.util.Slog;
+
+/**
+ * An implementation of {@link ActivityTaskManagerInternal.PackageConfigurationUpdater}.
+ */
+final class PackageConfigurationUpdaterImpl implements
+        ActivityTaskManagerInternal.PackageConfigurationUpdater {
+    private static final String TAG = "PackageConfigurationUpdaterImpl";
+    private final int mPid;
+    private Integer mNightMode;
+    private LocaleList mLocales;
+    private ActivityTaskManagerService mAtm;
+
+    PackageConfigurationUpdaterImpl(int pid, ActivityTaskManagerService atm) {
+        mPid = pid;
+        mAtm = atm;
+    }
+
+    @Override
+    public ActivityTaskManagerInternal.PackageConfigurationUpdater setNightMode(int nightMode) {
+        synchronized (this) {
+            mNightMode = nightMode;
+        }
+        return this;
+    }
+
+    @Override
+    public ActivityTaskManagerInternal.PackageConfigurationUpdater
+            setLocales(LocaleList locales) {
+        synchronized (this) {
+            mLocales = locales;
+        }
+        return this;
+    }
+
+    @Override
+    public void commit() {
+        synchronized (this) {
+            synchronized (mAtm.mGlobalLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    final WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid);
+                    if (wpc == null) {
+                        Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
+                        return;
+                    }
+                    LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+                            mLocales, mAtm.getGlobalConfiguration().getLocales());
+                    wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+                    wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
+                    mAtm.mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+    }
+
+    Integer getNightMode() {
+        return mNightMode;
+    }
+
+    LocaleList getLocales() {
+        return mLocales;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
new file mode 100644
index 0000000..ef8dee4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.util.Set;
+
+/**
+ * Maintains a map of possible {@link DisplayInfo} for displays and states that may be encountered
+ * on a device. This is not guaranteed to include all possible device states for all displays.
+ *
+ * By 'possible', this class only handles device states for displays and display groups it is
+ * currently aware of. It can not handle all eventual states the system may enter, for example, if
+ * an external display is added, or a new display is added to the group.
+ */
+public class PossibleDisplayInfoMapper {
+    private static final String TAG = "PossibleDisplayInfoMapper";
+    private static final boolean DEBUG = false;
+
+    private final DisplayManagerInternal mDisplayManagerInternal;
+
+    /**
+     * Map of all logical displays, indexed by logical display id.
+     * Each logical display has multiple entries, one for each possible rotation and device
+     * state.
+     *
+     * Emptied and re-calculated when a display is added, removed, or changed.
+     */
+    private final SparseArray<Set<DisplayInfo>> mDisplayInfos = new SparseArray<>();
+
+    PossibleDisplayInfoMapper(DisplayManagerInternal displayManagerInternal) {
+        mDisplayManagerInternal = displayManagerInternal;
+    }
+
+
+    /**
+     * Returns, for the given displayId, a set of display infos. Set contains the possible rotations
+     * for each supported device state.
+     */
+    public Set<DisplayInfo> getPossibleDisplayInfos(int displayId) {
+        // Update display infos before returning, since any cached values would have been removed
+        // in response to any display event. This model avoids re-computing the cache for every
+        // display change event (which occurs extremely frequently in the normal usage of the
+        // device).
+        updatePossibleDisplayInfos(displayId);
+        if (!mDisplayInfos.contains(displayId)) {
+            return new ArraySet<>();
+        }
+        return Set.copyOf(mDisplayInfos.get(displayId));
+    }
+
+    /**
+     * Updates the possible {@link DisplayInfo}s for the given display, by calculating the
+     * DisplayInfo for each rotation across supported device states.
+     */
+    public void updatePossibleDisplayInfos(int displayId) {
+        Set<DisplayInfo> displayInfos = mDisplayManagerInternal.getPossibleDisplayInfo(displayId);
+        if (DEBUG) {
+            Slog.v(TAG, "updatePossibleDisplayInfos, calculate rotations for given DisplayInfo "
+                    + displayInfos.size() + " on display " + displayId);
+        }
+        updateDisplayInfos(displayInfos);
+    }
+
+    /**
+     * For the given displayId, removes all possible {@link DisplayInfo}.
+     */
+    public void removePossibleDisplayInfos(int displayId) {
+        if (DEBUG && mDisplayInfos.get(displayId) != null) {
+            Slog.v(TAG, "onDisplayRemoved, remove all DisplayInfo (" + mDisplayInfos.get(
+                    displayId).size() + ") with id " + displayId);
+        }
+        mDisplayInfos.remove(displayId);
+    }
+
+    private void updateDisplayInfos(Set<DisplayInfo> displayInfos) {
+        // Empty out cache before re-computing.
+        mDisplayInfos.clear();
+        DisplayInfo[] originalDisplayInfos = new DisplayInfo[displayInfos.size()];
+        displayInfos.toArray(originalDisplayInfos);
+        // Iterate over each logical display layout for the current state.
+        Set<DisplayInfo> rotatedDisplayInfos;
+        for (DisplayInfo di : originalDisplayInfos) {
+            rotatedDisplayInfos = new ArraySet<>();
+            // Calculate all possible rotations for each logical display.
+            for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
+                rotatedDisplayInfos.add(applyRotation(di, rotation));
+            }
+            // Combine all results under the logical display id.
+            Set<DisplayInfo> priorDisplayInfos = mDisplayInfos.get(di.displayId, new ArraySet<>());
+            priorDisplayInfos.addAll(rotatedDisplayInfos);
+            mDisplayInfos.put(di.displayId, priorDisplayInfos);
+        }
+    }
+
+    private static DisplayInfo applyRotation(DisplayInfo displayInfo,
+            @Surface.Rotation int rotation) {
+        DisplayInfo updatedDisplayInfo = new DisplayInfo();
+        updatedDisplayInfo.copyFrom(displayInfo);
+        updatedDisplayInfo.rotation = rotation;
+
+        final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
+        final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
+        updatedDisplayInfo.logicalWidth = naturalWidth;
+        updatedDisplayInfo.logicalHeight = naturalHeight;
+        return updatedDisplayInfo;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 6b57ede..b90f937 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -436,6 +436,19 @@
         int getMode() {
             return mMode;
         }
+
+        /** Whether its parent is also an animation target in the same transition. */
+        boolean hasAnimatingParent() {
+            // mOpeningApps and mClosingApps are only activities, so only need to check
+            // mChangingContainers.
+            for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
+                if (mWindowContainer.isDescendantOf(
+                        mDisplayContent.mChangingContainers.valueAt(i))) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
     class RemoteAnimationAdapterWrapper implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c48dba4..4020788 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2577,6 +2577,9 @@
             if (mService.isBooted() || mService.isBooting()) {
                 startSystemDecorations(display.mDisplayContent);
             }
+            // Drop any cached DisplayInfos associated with this display id - the values are now
+            // out of date given this display added event.
+            mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
         }
     }
 
@@ -2597,8 +2600,8 @@
             if (displayContent == null) {
                 return;
             }
-
             displayContent.remove();
+            mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
         }
     }
 
@@ -2610,6 +2613,9 @@
             if (displayContent != null) {
                 displayContent.onDisplayChanged();
             }
+            // Drop any cached DisplayInfos associated with this display id - the values are now
+            // out of date given this display changed event.
+            mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 0b56777..8c056b2 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -299,6 +299,17 @@
         }
     }
 
+
+    @Override
+    public boolean dropForAccessibility(IWindow window, int x, int y) {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mDragDropController.dropForAccessibility(window, x, y);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     /**
      * Validates the given drag data.
      */
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 3c6c23b..c7bf8ecf 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -59,11 +59,30 @@
     @VisibleForTesting
     final Animatable mAnimatable;
     private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
+
+    /**
+     * Static callback to run on all animations started through this SurfaceAnimator
+     * when an animation on a Surface is finished or cancelled without restart.
+     */
     @VisibleForTesting
     @Nullable
     final OnAnimationFinishedCallback mStaticAnimationFinishedCallback;
+
+    /**
+     * Callback unique to each animation (i.e. AnimationAdapter). To be run when an animation on a
+     * Surface is finished or cancelled without restart.
+     */
     @Nullable
-    private OnAnimationFinishedCallback mAnimationFinishedCallback;
+    private OnAnimationFinishedCallback mSurfaceAnimationFinishedCallback;
+
+    /**
+     * The callback is triggered after the SurfaceAnimator sends a cancel call to the underlying
+     * AnimationAdapter.
+     * NOTE: Must be called wherever we call onAnimationCancelled on mAnimation.
+     */
+    @Nullable
+    private Runnable mAnimationCancelledCallback;
+
     private boolean mAnimationStartDelayed;
 
     /**
@@ -100,7 +119,7 @@
                         return;
                     }
                     final OnAnimationFinishedCallback animationFinishCallback =
-                            mAnimationFinishedCallback;
+                            mSurfaceAnimationFinishedCallback;
                     reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
                     if (staticAnimationFinishedCallback != null) {
                         staticAnimationFinishedCallback.onAnimationFinished(type, anim);
@@ -130,15 +149,19 @@
      *               This is important as it will start with the leash hidden or visible before
      *               handing it to the component that is responsible to run the animation.
      * @param animationFinishedCallback The callback being triggered when the animation finishes.
+     * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
+     *                                   cancel call to the underlying AnimationAdapter.
      */
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
             @AnimationType int type,
             @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+            @Nullable Runnable animationCancelledCallback,
             @Nullable SurfaceFreezer freezer) {
         cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
         mAnimation = anim;
         mAnimationType = type;
-        mAnimationFinishedCallback = animationFinishedCallback;
+        mSurfaceAnimationFinishedCallback = animationFinishedCallback;
+        mAnimationCancelledCallback = animationCancelledCallback;
         final SurfaceControl surface = mAnimatable.getSurfaceControl();
         if (surface == null) {
             Slog.w(TAG, "Unable to start animation, surface is null or no children.");
@@ -161,14 +184,9 @@
     }
 
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
-            @AnimationType int type,
-            @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
-        startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
-    }
-
-    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
             @AnimationType int type) {
-        startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
+        startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */,
+                null /* animationCancelledCallback */, null /* freezer */);
     }
 
     /**
@@ -278,7 +296,8 @@
         mLeash = from.mLeash;
         mAnimation = from.mAnimation;
         mAnimationType = from.mAnimationType;
-        mAnimationFinishedCallback = from.mAnimationFinishedCallback;
+        mSurfaceAnimationFinishedCallback = from.mSurfaceAnimationFinishedCallback;
+        mAnimationCancelledCallback = from.mAnimationCancelledCallback;
 
         // Cancel source animation, but don't let animation runner cancel the animation.
         from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
@@ -306,11 +325,16 @@
         final SurfaceControl leash = mLeash;
         final AnimationAdapter animation = mAnimation;
         final @AnimationType int animationType = mAnimationType;
-        final OnAnimationFinishedCallback animationFinishedCallback = mAnimationFinishedCallback;
+        final OnAnimationFinishedCallback animationFinishedCallback =
+                mSurfaceAnimationFinishedCallback;
+        final Runnable animationCancelledCallback = mAnimationCancelledCallback;
         reset(t, false);
         if (animation != null) {
             if (!mAnimationStartDelayed && forwardCancel) {
                 animation.onAnimationCancelled(leash);
+                if (animationCancelledCallback != null) {
+                    animationCancelledCallback.run();
+                }
             }
             if (!restarting) {
                 if (mStaticAnimationFinishedCallback != null) {
@@ -335,7 +359,7 @@
     private void reset(Transaction t, boolean destroyLeash) {
         mService.mAnimationTransferMap.remove(mAnimation);
         mAnimation = null;
-        mAnimationFinishedCallback = null;
+        mSurfaceAnimationFinishedCallback = null;
         mAnimationType = ANIMATION_TYPE_NONE;
         if (mLeash == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ca98564..3f6708d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -130,7 +130,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -2348,11 +2347,6 @@
         return getRootTask().mTaskId;
     }
 
-    @Nullable
-    Task getRootTask() {
-        return getRootTaskFragment().asTask();
-    }
-
     /** @return the first organized task. */
     @Nullable
     Task getOrganizedTask() {
@@ -2984,16 +2978,6 @@
         super.resetSurfacePositionForAnimationLeash(t);
     }
 
-    @Override
-    Rect getAnimationBounds(int appRootTaskClipMode) {
-        // TODO(b/131661052): we should remove appRootTaskClipMode with hierarchical animations.
-        if (appRootTaskClipMode == ROOT_TASK_CLIP_BEFORE_ANIM && getRootTask() != null) {
-            // Using the root task bounds here effectively applies the clipping before animation.
-            return getRootTask().getBounds();
-        }
-        return super.getAnimationBounds(appRootTaskClipMode);
-    }
-
     boolean shouldAnimate() {
         /**
          * Animations are handled by the TaskOrganizer implementation.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4a1a922..a257e90 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -42,6 +42,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import static java.lang.Integer.MIN_VALUE;
+
+import android.annotation.ColorInt;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
@@ -80,6 +83,22 @@
     DisplayContent mDisplayContent;
 
     /**
+     * A color layer that serves as a solid color background to certain animations.
+     */
+    private SurfaceControl mColorBackgroundLayer;
+
+    /**
+     * This counter is used to make sure we don't prematurely clear the background color in the
+     * case that background color animations are interleaved.
+     * NOTE: The last set color will remain until the counter is reset to 0, which means that an
+     * animation background color may sometime remain after the animation has finished through an
+     * animation with a different background color if an animation starts after and ends before
+     * another where both set different background colors. However, this is not a concern as
+     * currently all task animation backgrounds are the same color.
+     */
+    private int mColorLayerCounter = 0;
+
+    /**
      * A control placed at the appropriate level for transitions to occur.
      */
     private SurfaceControl mAppAnimationLayer;
@@ -959,6 +978,11 @@
     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
         if (getParent() != null) {
             super.onParentChanged(newParent, oldParent, () -> {
+                mColorBackgroundLayer = makeChildSurface(null)
+                        .setColorLayer()
+                        .setName("colorBackgroundLayer")
+                        .setCallsite("TaskDisplayArea.onParentChanged")
+                        .build();
                 mAppAnimationLayer = makeChildSurface(null)
                         .setName("animationLayer")
                         .setCallsite("TaskDisplayArea.onParentChanged")
@@ -975,6 +999,7 @@
                         .setName("splitScreenDividerAnchor")
                         .setCallsite("TaskDisplayArea.onParentChanged")
                         .build();
+
                 getSyncTransaction()
                         .show(mAppAnimationLayer)
                         .show(mBoostedAppAnimationLayer)
@@ -984,11 +1009,13 @@
         } else {
             super.onParentChanged(newParent, oldParent);
             mWmService.mTransactionFactory.get()
+                    .remove(mColorBackgroundLayer)
                     .remove(mAppAnimationLayer)
                     .remove(mBoostedAppAnimationLayer)
                     .remove(mHomeAppAnimationLayer)
                     .remove(mSplitScreenDividerAnchor)
                     .apply();
+            mColorBackgroundLayer = null;
             mAppAnimationLayer = null;
             mBoostedAppAnimationLayer = null;
             mHomeAppAnimationLayer = null;
@@ -996,6 +1023,39 @@
         }
     }
 
+    void setBackgroundColor(@ColorInt int color) {
+        if (mColorBackgroundLayer == null) {
+            return;
+        }
+
+        float r = ((color >> 16) & 0xff) / 255.0f;
+        float g = ((color >>  8) & 0xff) / 255.0f;
+        float b = ((color >>  0) & 0xff) / 255.0f;
+        float a = ((color >> 24) & 0xff) / 255.0f;
+
+        mColorLayerCounter++;
+
+        getPendingTransaction().setLayer(mColorBackgroundLayer, MIN_VALUE)
+                .setColor(mColorBackgroundLayer, new float[]{r, g, b})
+                .setAlpha(mColorBackgroundLayer, a)
+                .setWindowCrop(mColorBackgroundLayer, getSurfaceWidth(), getSurfaceHeight())
+                .setPosition(mColorBackgroundLayer, 0, 0)
+                .show(mColorBackgroundLayer);
+
+        scheduleAnimation();
+    }
+
+    void clearBackgroundColor() {
+        mColorLayerCounter--;
+
+        // Only clear the color layer if we have received the same amounts of clear as set
+        // requests.
+        if (mColorLayerCounter == 0) {
+            getPendingTransaction().hide(mColorBackgroundLayer);
+            scheduleAnimation();
+        }
+    }
+
     @Override
     void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
         super.migrateToNewSurfaceControl(t);
@@ -1004,6 +1064,7 @@
         }
 
         // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
+        t.reparent(mColorBackgroundLayer, mSurfaceControl);
         t.reparent(mAppAnimationLayer, mSurfaceControl);
         t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
         t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
@@ -2159,6 +2220,11 @@
     }
 
     @Override
+    TaskDisplayArea getTaskDisplayArea() {
+        return this;
+    }
+
+    @Override
     boolean isTaskDisplayArea() {
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c8b8b0d..fce279d 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -33,7 +33,6 @@
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_NONE;
@@ -460,12 +459,17 @@
         return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
     }
 
+    @Nullable
+    Task getRootTask() {
+        return getRootTaskFragment().asTask();
+    }
+
     @Override
     TaskFragment asTaskFragment() {
         return this;
     }
 
-    /** Returns {@code true} if this is a container for embedded activities or tasks. */
+    @Override
     boolean isEmbedded() {
         if (mIsEmbedded) {
             return true;
@@ -579,9 +583,6 @@
             isPausingDied = true;
         }
         if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
-            if (mLastPausedActivity.isNoHistory()) {
-                mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
-            }
             mLastPausedActivity = null;
         }
         return isPausingDied;
@@ -2047,9 +2048,7 @@
 
         if (shouldStartChangeTransition(mTmpPrevBounds)) {
             initializeChangeTransition(mTmpPrevBounds);
-        }
-
-        if (mTaskFragmentOrganizer != null) {
+        } else if (mTaskFragmentOrganizer != null) {
             // Update the surface position here instead of in the organizer so that we can make sure
             // it can be synced with the surface freezer.
             updateSurfacePosition(getSyncTransaction());
@@ -2073,15 +2072,6 @@
         return !startBounds.equals(getBounds());
     }
 
-    /**
-     * Initializes a change transition. See {@link SurfaceFreezer} for more information.
-     */
-    void initializeChangeTransition(Rect startBounds) {
-        mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
-        mDisplayContent.mChangingContainers.add(this);
-        mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
-    }
-
     @Override
     void setSurfaceControl(SurfaceControl sc) {
         super.setSurfaceControl(sc);
@@ -2163,6 +2153,18 @@
         return mTaskFragmentOrganizer != null;
     }
 
+    /** Whether this is an organized {@link TaskFragment} and not a {@link Task}. */
+    final boolean isOrganizedTaskFragment() {
+        return mTaskFragmentOrganizer != null;
+    }
+
+    boolean isReadyToTransit() {
+        // We don't want to start the transition if the organized TaskFragment is empty, unless
+        // it is requested to be removed.
+        return !isOrganizedTaskFragment() || getTopNonFinishingActivity() != null
+                || mIsRemovalRequested;
+    }
+
     /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
     void clearLastPausedActivity() {
         forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 63951d9..5ce9938 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -168,6 +168,9 @@
      */
     @VisibleForTesting
     void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
+        if (shouldDisableSnapshots()) {
+            return;
+        }
         mSkipClosingAppSnapshotTasks.addAll(tasks);
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1a46d0f..1909875 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -58,6 +58,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.content.pm.ActivityInfo;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -269,6 +270,18 @@
         mChanges.get(wc).mExistenceChanged = true;
     }
 
+    /**
+     * Specifies configuration change explicitly for the window container, so it can be chosen as
+     * transition target. This is usually used with transition mode
+     * {@link android.view.WindowManager#TRANSIT_CHANGE}.
+     */
+    void setKnownConfigChanges(WindowContainer<?> wc, @ActivityInfo.Config int changes) {
+        final ChangeInfo changeInfo = mChanges.get(wc);
+        if (changeInfo != null) {
+            changeInfo.mKnownConfigChanges = changes;
+        }
+    }
+
     private void sendRemoteCallback(@Nullable IRemoteCallback callback) {
         if (callback == null) return;
         mController.mAtm.mH.sendMessage(PooledLambda.obtainMessage(cb -> {
@@ -1218,6 +1231,7 @@
         final Rect mAbsoluteBounds = new Rect();
         boolean mShowWallpaper;
         int mRotation = ROTATION_UNDEFINED;
+        @ActivityInfo.Config int mKnownConfigChanges;
 
         ChangeInfo(@NonNull WindowContainer origState) {
             mVisible = origState.isVisibleRequested();
@@ -1240,6 +1254,7 @@
             final boolean currVisible = newState.isVisibleRequested();
             if (currVisible == mVisible && !mVisible) return false;
             return currVisible != mVisible
+                    || mKnownConfigChanges != 0
                     // if mWindowingMode is 0, this container wasn't attached at collect time, so
                     // assume no change in windowing-mode.
                     || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode)
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c1d0f80..fc54239 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -71,15 +71,7 @@
 
     final Lock mRunningLock = new Lock();
 
-    private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
-        // clean-up/finish any playing transitions.
-        for (int i = 0; i < mPlayingTransitions.size(); ++i) {
-            mPlayingTransitions.get(i).cleanUpOnFailure();
-        }
-        mPlayingTransitions.clear();
-        mTransitionPlayer = null;
-        mRunningLock.doNotifyLocked();
-    };
+    private final IBinder.DeathRecipient mTransitionPlayerDeath;
 
     /** The transition currently being constructed (collecting participants). */
     private Transition mCollectingTransition = null;
@@ -90,6 +82,17 @@
     TransitionController(ActivityTaskManagerService atm) {
         mAtm = atm;
         mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
+        mTransitionPlayerDeath = () -> {
+            synchronized (mAtm.mGlobalLock) {
+                // Clean-up/finish any playing transitions.
+                for (int i = 0; i < mPlayingTransitions.size(); ++i) {
+                    mPlayingTransitions.get(i).cleanUpOnFailure();
+                }
+                mPlayingTransitions.clear();
+                mTransitionPlayer = null;
+                mRunningLock.doNotifyLocked();
+            }
+        };
     }
 
     /** @see #createTransition(int, int) */
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index ec4eb5d..08758af 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
+import android.annotation.ColorInt;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.SystemClock;
@@ -85,6 +86,12 @@
     }
 
     @Override
+    @ColorInt
+    public int getBackgroundColor() {
+        return mAnimation.getBackgroundColor();
+    }
+
+    @Override
     public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
         final TmpValues tmp = mThreadLocalTmps.get();
         tmp.transformation.clear();
@@ -118,16 +125,30 @@
     @Override
     public long calculateStatusBarTransitionStartTime() {
         TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);
-        if (openTranslateAnimation != null) {
 
-            // Some interpolators are extremely quickly mostly finished, but not completely. For
-            // our purposes, we need to find the fraction for which ther interpolator is mostly
-            // there, and use that value for the calculation.
-            float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
-            return SystemClock.uptimeMillis()
-                    + openTranslateAnimation.getStartOffset()
-                    + (long)(openTranslateAnimation.getDuration() * t)
-                    - STATUS_BAR_TRANSITION_DURATION;
+        if (openTranslateAnimation != null) {
+            if (openTranslateAnimation.isXAxisTransition()
+                    && openTranslateAnimation.isFullWidthTranslate()) {
+                // On X axis transitions that are fullscreen (heuristic for task like transitions)
+                // we want the status bar to animate right in the middle of the translation when
+                // the windows/tasks have each moved half way across.
+                float t = findMiddleOfTranslationFraction(openTranslateAnimation.getInterpolator());
+
+                return SystemClock.uptimeMillis()
+                        + openTranslateAnimation.getStartOffset()
+                        + (long) (openTranslateAnimation.getDuration() * t)
+                        - (long) (STATUS_BAR_TRANSITION_DURATION * 0.5);
+            } else {
+                // Some interpolators are extremely quickly mostly finished, but not completely. For
+                // our purposes, we need to find the fraction for which their interpolator is mostly
+                // there, and use that value for the calculation.
+                float t = findAlmostThereFraction(openTranslateAnimation.getInterpolator());
+
+                return SystemClock.uptimeMillis()
+                        + openTranslateAnimation.getStartOffset()
+                        + (long) (openTranslateAnimation.getDuration() * t)
+                        - STATUS_BAR_TRANSITION_DURATION;
+            }
         } else {
             return SystemClock.uptimeMillis();
         }
@@ -176,20 +197,39 @@
     }
 
     /**
-     * Binary searches for a {@code t} such that there exists a {@code -0.01 < eps < 0.01} for which
-     * {@code interpolator(t + eps) > 0.99}.
+     * Finds the fraction of the animation's duration at which the transition is almost done with a
+     * maximal error of 0.01 when it is animated with {@code interpolator}.
      */
     private static float findAlmostThereFraction(Interpolator interpolator) {
+        return findInterpolationAdjustedTargetFraction(interpolator, 0.99f, 0.01f);
+    }
+
+    /**
+     * Finds the fraction of the animation's duration at which the transition is spacially half way
+     * done with a maximal error of 0.01 when it is animated with {@code interpolator}.
+     */
+    private float findMiddleOfTranslationFraction(Interpolator interpolator) {
+        return findInterpolationAdjustedTargetFraction(interpolator, 0.5f, 0.01f);
+    }
+
+    /**
+     * Binary searches for a {@code val} such that there exists an {@code -0.01 < epsilon < 0.01}
+     * for which {@code interpolator(val + epsilon) > target}.
+     */
+    private static float findInterpolationAdjustedTargetFraction(
+            Interpolator interpolator, float target, float epsilon) {
         float val = 0.5f;
         float adj = 0.25f;
-        while (adj >= 0.01f) {
-            if (interpolator.getInterpolation(val) < 0.99f) {
+
+        while (adj >= epsilon) {
+            if (interpolator.getInterpolation(val) < target) {
                 val += adj;
             } else {
                 val -= adj;
             }
             adj /= 2;
         }
+
         return val;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cdfa5aa..5464415 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -30,6 +31,11 @@
 import static android.os.UserHandle.USER_NULL;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -59,10 +65,13 @@
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 
 import android.annotation.CallSuper;
+import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.app.WindowConfiguration;
+import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
@@ -88,6 +97,7 @@
 import android.window.IWindowContainerToken;
 import android.window.WindowContainerToken;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
@@ -854,6 +864,12 @@
         return parent != null ? parent.getRootDisplayArea() : null;
     }
 
+    @Nullable
+    TaskDisplayArea getTaskDisplayArea() {
+        WindowContainer parent = getParent();
+        return parent != null ? parent.getTaskDisplayArea() : null;
+    }
+
     boolean isAttached() {
         WindowContainer parent = getParent();
         return parent != null && parent.isAttached();
@@ -2549,10 +2565,13 @@
      *               some point but the meaning is too weird to work for all containers.
      * @param type The type of animation defined as {@link AnimationType}.
      * @param animationFinishedCallback The callback being triggered when the animation finishes.
+     * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
+     *                                   cancel call to the underlying AnimationAdapter.
      */
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
             @AnimationType int type,
-            @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+            @Nullable Runnable animationCancelledCallback) {
         if (DEBUG_ANIM) {
             Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
         }
@@ -2560,7 +2579,14 @@
         // TODO: This should use isVisible() but because isVisible has a really weird meaning at
         // the moment this doesn't work for all animatable window containers.
         mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
-                mSurfaceFreezer);
+                animationCancelledCallback, mSurfaceFreezer);
+    }
+
+    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+            @AnimationType int type,
+            @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+        startAnimation(t, anim, hidden, type, animationFinishedCallback,
+                null /* adapterAnimationCancelledCallback */);
     }
 
     void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -2578,13 +2604,34 @@
         mSurfaceFreezer.unfreeze(getPendingTransaction());
     }
 
+    /**
+     * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+     *
+     * For now, this will only be called for the following cases:
+     * 1. {@link Task} is changing windowing mode between fullscreen and freeform.
+     * 2. {@link TaskFragment} is organized and is changing window bounds.
+     * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
+     *
+     * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
+     */
+    void initializeChangeTransition(Rect startBounds) {
+        mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
+        mDisplayContent.mChangingContainers.add(this);
+        mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
+    }
+
     ArraySet<WindowContainer> getAnimationSources() {
         return mSurfaceAnimationSources;
     }
 
     @Override
     public SurfaceControl getFreezeSnapshotTarget() {
-        return null;
+        // Only allow freezing if this window is in a TRANSIT_CHANGE
+        if (!mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)
+                || !mDisplayContent.mChangingContainers.contains(this)) {
+            return null;
+        }
+        return getSurfaceControl();
     }
 
     @Override
@@ -2773,8 +2820,26 @@
             if (sources != null) {
                 mSurfaceAnimationSources.addAll(sources);
             }
+
+            TaskDisplayArea taskDisplayArea = getTaskDisplayArea();
+            boolean isSettingBackgroundColor = taskDisplayArea != null
+                    && isTransitionWithBackgroundColor(transit);
+
+            if (isSettingBackgroundColor) {
+                Context uiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+                @ColorInt int backgroundColor = uiContext.getColor(R.color.overview_background);
+
+                taskDisplayArea.setBackgroundColor(backgroundColor);
+            }
+
+            final Runnable cleanUpCallback = isSettingBackgroundColor
+                    ? taskDisplayArea::clearBackgroundColor : () -> {};
+
             startAnimation(getPendingTransaction(), adapter, !isVisible(),
-                    ANIMATION_TYPE_APP_TRANSITION);
+                    ANIMATION_TYPE_APP_TRANSITION,
+                    (type, anim) -> cleanUpCallback.run(),
+                    cleanUpCallback);
+
             if (adapter.getShowWallpaper()) {
                 getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
             }
@@ -2785,6 +2850,13 @@
         }
     }
 
+    private boolean isTransitionWithBackgroundColor(@TransitionOldType int transit) {
+        return transit == TRANSIT_OLD_TASK_OPEN
+                || transit == TRANSIT_OLD_TASK_CLOSE
+                || transit == TRANSIT_OLD_TASK_TO_FRONT
+                || transit == TRANSIT_OLD_TASK_TO_BACK;
+    }
+
     final SurfaceAnimationRunner getSurfaceAnimationRunner() {
         return mWmService.mSurfaceAnimationRunner;
     }
@@ -2793,7 +2865,8 @@
                                     boolean isVoiceInteraction) {
         if (isOrganized()
                 // TODO(b/161711458): Clean-up when moved to shell.
-                && getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+                && getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+                && getWindowingMode() != WINDOWING_MODE_FREEFORM) {
             return null;
         }
 
@@ -3189,6 +3262,11 @@
         return false;
     }
 
+    /** @return {@code true} if this is a container for embedded activities or tasks. */
+    boolean isEmbedded() {
+        return false;
+    }
+
     /**
      * @return {@code true} if this container's surface should be shown when it is created.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 907098e..b9c6e1b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -46,8 +46,6 @@
 import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -174,8 +172,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.hardware.configstore.V1_0.ISurfaceFlingerConfigs;
 import android.hardware.configstore.V1_0.OptionalBool;
+import android.hardware.configstore.V1_1.DisplayOrientation;
+import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
+import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
@@ -227,6 +227,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 import android.view.Display;
+import android.view.DisplayAddress;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -326,13 +327,11 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -454,6 +453,8 @@
      */
     static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
             SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
+    private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0;
+    private DisplayAddress mPrimaryDisplayPhysicalAddress;
 
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
@@ -1054,6 +1055,10 @@
 
     final HighRefreshRateDenylist mHighRefreshRateDenylist;
 
+    // Maintainer of a collection of all possible DisplayInfo for all configurations of the
+    // logical displays.
+    final PossibleDisplayInfoMapper mPossibleDisplayInfoMapper;
+
     // If true, only the core apps and services are being launched because the device
     // is in a special boot mode, such as being encrypted or waiting for a decryption password.
     // For example, when this flag is true, there will be no wallpaper service.
@@ -1229,6 +1234,7 @@
 
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+        mPossibleDisplayInfoMapper = new PossibleDisplayInfoMapper(mDisplayManagerInternal);
 
         mSurfaceControlFactory = surfaceControlFactory;
         mTransactionFactory = transactionFactory;
@@ -2458,16 +2464,21 @@
             configChanged = displayContent.updateOrientation();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
-            final DisplayInfo rotatedDisplayInfo =
-                    win.mToken.getFixedRotationTransformDisplayInfo();
-            if (rotatedDisplayInfo != null) {
-                outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
-            } else {
-                // We have to update the transform hint of display here, but we need to get if from
-                // SurfaceFlinger, so set it as rotation of display for most cases, then
-                // SurfaceFlinger would still update the transform hint of display in next frame.
-                outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
+            final DisplayInfo displayInfo = win.getDisplayInfo();
+            int transformHint = displayInfo.rotation;
+            // If the window is on the primary display, use the panel orientation to adjust the
+            // transform hint
+            final boolean isPrimaryDisplay = displayInfo.address != null &&
+                    displayInfo.address.equals(mPrimaryDisplayPhysicalAddress);
+            if (isPrimaryDisplay) {
+                transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
             }
+            outSurfaceControl.setTransformHint(transformHint);
+            ProtoLog.v(WM_DEBUG_ORIENTATION,
+                    "Passing transform hint %d for window %s%s",
+                    transformHint, win,
+                    isPrimaryDisplay ? " on primary display with orientation "
+                            + mPrimaryDisplayOrientation : "");
 
             if (toBeDisplayed && win.mIsWallpaper) {
                 displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
@@ -4873,6 +4884,9 @@
         mTaskSnapshotController.systemReady();
         mHasWideColorGamutSupport = queryWideColorGamutSupport();
         mHasHdrSupport = queryHdrSupport();
+        mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
+        mPrimaryDisplayPhysicalAddress =
+            DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
         UiThread.getHandler().post(mSettingsObserver::loadSettings);
         IVrManager vrManager = IVrManager.Stub.asInterface(
                 ServiceManager.getService(Context.VR_SERVICE));
@@ -4892,6 +4906,9 @@
         }
     }
 
+
+    // Keep logic in sync with SurfaceFlingerProperties.cpp
+    // Consider exposing properties via ISurfaceComposer instead.
     private static boolean queryWideColorGamutSupport() {
         boolean defaultValue = false;
         Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display();
@@ -4932,6 +4949,39 @@
         return false;
     }
 
+    private static @Surface.Rotation int queryPrimaryDisplayOrientation() {
+        Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop =
+                SurfaceFlingerProperties.primary_display_orientation();
+        if (prop.isPresent()) {
+            switch (prop.get()) {
+                case ORIENTATION_90: return Surface.ROTATION_90;
+                case ORIENTATION_180: return Surface.ROTATION_180;
+                case ORIENTATION_270: return Surface.ROTATION_270;
+                case ORIENTATION_0:
+                default:
+                    return Surface.ROTATION_0;
+            }
+        }
+        try {
+            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
+            OptionalDisplayOrientation primaryDisplayOrientation =
+                    surfaceFlinger.primaryDisplayOrientation();
+            if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) {
+                switch (primaryDisplayOrientation.value) {
+                    case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90;
+                    case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180;
+                    case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270;
+                    case DisplayOrientation.ORIENTATION_0:
+                    default:
+                        return Surface.ROTATION_0;
+                }
+            }
+        } catch (Exception e) {
+            // Use default value if we can't talk to config store.
+        }
+        return Surface.ROTATION_0;
+    }
+
     void reportFocusChanged(IBinder oldToken, IBinder newToken) {
         WindowState lastFocus;
         WindowState newFocus;
@@ -8467,23 +8517,10 @@
                             + " for getPossibleMaximumWindowMetrics");
                     return new ArrayList<>();
                 }
-                // TODO(181127261) DisplayInfo should be pushed from DisplayManager.
-                final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                if (dc == null) {
-                    Slog.e(TAG, "Invalid displayId " + displayId
-                            + " for getPossibleMaximumWindowMetrics");
-                    return new ArrayList<>();
-                }
 
-                // TODO(181127261) DisplayManager should provide a DisplayInfo for each rotation
-                DisplayInfo currentDisplayInfo = dc.getDisplayInfo();
-                Set<DisplayInfo> displayInfoSet = new HashSet<>();
-                for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
-                    currentDisplayInfo.rotation = rotation;
-                    // TODO(181127261) Retrieve the device state from display stack.
-                    displayInfoSet.add(new DisplayInfo(currentDisplayInfo));
-                }
-                return new ArrayList<DisplayInfo>(displayInfoSet);
+                // Retrieve the DisplayInfo for all possible rotations across all possible display
+                // layouts.
+                return List.copyOf(mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId));
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 834b6e6..4cc764a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -122,7 +122,8 @@
      * A Map which manages the relationship between
      * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
      */
-    private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
+    @VisibleForTesting
+    final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
 
     WindowOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
@@ -326,7 +327,7 @@
             if (transition != null) {
                 // First check if we have a display rotation transition and if so, update it.
                 final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
-                if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) {
+                if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) {
                     // Go through all tasks and collect them before the rotation
                     // TODO(shell-transitions): move collect() to onConfigurationChange once
                     //       wallpaper handling is synchronized.
@@ -672,7 +673,7 @@
                     throw new IllegalArgumentException(
                             "Can only delete organized TaskFragment, but not Task.");
                 }
-                deleteTaskFragment(taskFragment, errorCallbackToken);
+                effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
                 break;
             case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
                 fragmentToken = hop.getContainer();
@@ -704,6 +705,7 @@
                     break;
                 }
                 activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                 break;
             case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
                 final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
@@ -716,6 +718,7 @@
                     break;
                 }
                 reparentTaskFragment(oldParent, newParent, errorCallbackToken);
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
                 break;
             case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
                 fragmentToken = hop.getContainer();
@@ -731,6 +734,7 @@
                     break;
                 }
                 tf1.setAdjacentTaskFragment(tf2);
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
 
                 final Bundle bundle = hop.getLaunchOptions();
                 final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
@@ -1175,7 +1179,7 @@
         }
     }
 
-    void deleteTaskFragment(@NonNull TaskFragment taskFragment,
+    private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
             @Nullable IBinder errorCallbackToken) {
         final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
         if (index < 0) {
@@ -1184,10 +1188,11 @@
                             + "taskFragment");
             sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
                     errorCallbackToken, exception);
-            return;
+            return 0;
         }
         mLaunchTaskFragments.removeAt(index);
         taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
+        return TRANSACT_EFFECTS_LIFECYCLE;
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2ccbf40..c3d51d5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -26,7 +26,6 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.graphics.GraphicsProtos.dumpPointProto;
 import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
-import static android.hardware.input.InputManager.BLOCK_UNTRUSTED_TOUCHES;
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -196,7 +195,6 @@
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
-import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
@@ -1172,9 +1170,6 @@
     }
 
     int getTouchOcclusionMode() {
-        if (!CompatChanges.isChangeEnabled(BLOCK_UNTRUSTED_TOUCHES, mOwnerUid)) {
-            return TouchOcclusionMode.ALLOW;
-        }
         if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
             return TouchOcclusionMode.USE_OPACITY;
         }
@@ -2582,8 +2577,7 @@
                     }
                 }
                 final boolean isAnimating = mAnimatingExit
-                        || isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)
-                        && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart());
+                        || isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES);
                 final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
                         && mActivityRecord.isLastWindow(this);
                 // We delay the removal of a window if it has a showing surface that can be used to run
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a0dc247..d4d8971 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -80,16 +80,10 @@
     static final int ROOT_TASK_CLIP_AFTER_ANIM = 0;
 
     /**
-     * Mode how the window gets clipped by the root task bounds: The clipping should be applied
-     * before applying the animation transformation, i.e. the root task bounds move with the window.
-     */
-    static final int ROOT_TASK_CLIP_BEFORE_ANIM = 1;
-
-    /**
      * Mode how window gets clipped by the root task bounds during an animation: Don't clip the
      * window by the root task bounds.
      */
-    static final int ROOT_TASK_CLIP_NONE = 2;
+    static final int ROOT_TASK_CLIP_NONE = 1;
 
     // Unchanging local convenience fields.
     final WindowManagerService mService;
@@ -256,10 +250,10 @@
             // away.
             if (mLastHidden && mDrawState != NO_SURFACE && !forceApplyNow) {
                 mPostDrawTransaction.merge(postDrawTransaction);
-                layoutNeeded = true;
             } else {
                 mWin.getSyncTransaction().merge(postDrawTransaction);
             }
+            layoutNeeded = true;
         }
 
         return layoutNeeded;
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index fd364ae7..51fa851 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -37,6 +37,8 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.os.Bundle;
 import android.os.RecoverySystem;
@@ -83,6 +85,8 @@
     private static final String CALLING_PACKAGE1 = "com.package.name1";
     private static final String CALLING_PACKAGE2 = "com.package.name2";
     private static final String CALLING_PACKAGE3 = "com.package.name3";
+    private static final String PERSISTENT_PACKAGE = "com.persistent.package";
+    private static final String NON_PERSISTENT_PACKAGE = "com.nonpersistent.package";
     private static final String NAMESPACE1 = "namespace1";
     private static final String NAMESPACE2 = "namespace2";
     private static final String NAMESPACE3 = "namespace3";
@@ -103,6 +107,8 @@
     private PackageWatchdog mMockPackageWatchdog;
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private ContentResolver mMockContentResolver;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private PackageManager mPackageManager;
 
     @Captor
     private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
@@ -129,6 +135,17 @@
         mNamespacesWiped = new HashSet<>();
 
         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+        ApplicationInfo persistentApplicationInfo = new ApplicationInfo();
+        persistentApplicationInfo.flags |=
+                ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT;
+
+        // If the package name is PERSISTENT_PACKAGE, then set the flags to be persistent and
+        // system. Don't set any flags otherwise.
+        when(mPackageManager.getApplicationInfo(eq(PERSISTENT_PACKAGE),
+                anyInt())).thenReturn(persistentApplicationInfo);
+        when(mPackageManager.getApplicationInfo(eq(NON_PERSISTENT_PACKAGE),
+                anyInt())).thenReturn(new ApplicationInfo());
         // Reset observer instance to get new mock context on every run
         RescuePartyObserver.reset();
 
@@ -241,29 +258,53 @@
 
     @Test
     public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
-        notePersistentAppCrash(1);
+        noteAppCrash(1, true);
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                 /*configResetVerifiedTimesMap=*/ null);
 
-        notePersistentAppCrash(2);
+        noteAppCrash(2, true);
 
         verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
                 /*configResetVerifiedTimesMap=*/ null);
 
-        notePersistentAppCrash(3);
+        noteAppCrash(3, true);
 
         verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
                 /*configResetVerifiedTimesMap=*/ null);
 
-        notePersistentAppCrash(4);
+        noteAppCrash(4, true);
         assertTrue(RescueParty.isRebootPropertySet());
 
-        notePersistentAppCrash(5);
+        noteAppCrash(5, true);
         assertTrue(RescueParty.isFactoryResetPropertySet());
     }
 
     @Test
+    public void testNonPersistentAppOnlyPerformsFlagResets() {
+        noteAppCrash(1, false);
+
+        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
+                /*configResetVerifiedTimesMap=*/ null);
+
+        noteAppCrash(2, false);
+
+        verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null,
+                /*configResetVerifiedTimesMap=*/ null);
+
+        noteAppCrash(3, false);
+
+        verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null,
+                /*configResetVerifiedTimesMap=*/ null);
+
+        noteAppCrash(4, false);
+        assertFalse(RescueParty.isRebootPropertySet());
+
+        noteAppCrash(5, false);
+        assertFalse(RescueParty.isFactoryResetPropertySet());
+    }
+
+    @Test
     public void testNonPersistentAppCrashDetectionWithScopedResets() {
         RescueParty.onSettingsProviderPublished(mMockContext);
         verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
@@ -311,11 +352,11 @@
 
         observer.execute(new VersionedPackage(
                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
-        assertTrue(RescueParty.isRebootPropertySet());
+        assertFalse(RescueParty.isRebootPropertySet());
 
         observer.execute(new VersionedPackage(
                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
-        assertTrue(RescueParty.isFactoryResetPropertySet());
+        assertFalse(RescueParty.isFactoryResetPropertySet());
     }
 
     @Test
@@ -376,11 +417,11 @@
 
         observer.execute(new VersionedPackage(
                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
-        assertTrue(RescueParty.isRebootPropertySet());
+        assertFalse(RescueParty.isRebootPropertySet());
 
         observer.execute(new VersionedPackage(
                 CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
-        assertTrue(RescueParty.isFactoryResetPropertySet());
+        assertFalse(RescueParty.isFactoryResetPropertySet());
     }
 
     @Test
@@ -627,9 +668,10 @@
         RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount);
     }
 
-    private void notePersistentAppCrash(int mitigationCount) {
+    private void noteAppCrash(int mitigationCount, boolean isPersistent) {
+        String packageName = isPersistent ? PERSISTENT_PACKAGE : NON_PERSISTENT_PACKAGE;
         RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
-                "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
+                packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
     }
 
     private Bundle getConfigAccessBundle(String callingPackage, String namespace) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 022fadc..609768c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -21,12 +21,16 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
+import android.app.ActivityManager;
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManagerInternal;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -48,8 +52,14 @@
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
-import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
@@ -60,8 +70,11 @@
  * Build/Install/Run:
  * atest FrameworksMockingServicesTests:CacheOomRankerTest
  */
+@SuppressWarnings("GuardedBy") // No tests are concurrent, so no need to test locking.
 @RunWith(MockitoJUnitRunner.class)
 public class CacheOomRankerTest {
+    private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
+            ZoneOffset.UTC).toInstant();
 
     @Mock
     private AppOpsService mAppOpsService;
@@ -82,6 +95,7 @@
     private int mNextUid = 30000;
     private int mNextPackageUid = 40000;
     private int mNextPackageName = 1;
+    private Map<Integer, Long> mPidToRss;
 
     private TestExecutor mExecutor = new TestExecutor();
     private CacheOomRanker mCacheOomRanker;
@@ -107,7 +121,15 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
 
-        mCacheOomRanker = new CacheOomRanker(mAms);
+        mPidToRss = new HashMap<>();
+        mCacheOomRanker = new CacheOomRanker(
+                mAms,
+                pid -> {
+                    Long rss = mPidToRss.get(pid);
+                    assertThat(rss).isNotNull();
+                    return new long[]{rss};
+                }
+        );
         mCacheOomRanker.init(mExecutor);
     }
 
@@ -136,6 +158,15 @@
 
         mExecutor.init();
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+                Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
+                false);
+        mExecutor.waitForLatch();
+        assertThat(mCacheOomRanker.mPreserveTopNApps)
+                .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
+
+        mExecutor.init();
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
                 Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f),
                 false);
@@ -165,6 +196,9 @@
     @Test
     public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 5,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */1.0f);
@@ -172,36 +206,40 @@
         ProcessList list = new ProcessList();
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(lastUsed40MinutesAgo);
         ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(lastUsed42MinutesAgo);
         ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
         processList.add(lastUsed60MinutesAgo);
         ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(lastUsed15MinutesAgo);
         ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
         processList.add(lastUsed17MinutesAgo);
         // Only re-ranking 5 entries so this should stay in most recent position.
         ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
         processList.add(lastUsed30MinutesAgo);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 5 ordered by least recently used first, then last processes position unchanged.
         assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
                 lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
-                lastUsed30MinutesAgo);
+                lastUsed30MinutesAgo).inOrder();
     }
 
     @Test
     public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 0.0f,
                 /* pssWeight= */ 1.0f,
                 /* lruWeight= */ 0.0f);
@@ -209,145 +247,459 @@
         ProcessList list = new ProcessList();
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(rss10k);
         ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(rss20k);
         ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
         processList.add(rss1k);
         ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(rss100k);
         ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
         processList.add(rss2k);
         ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
         processList.add(rss15k);
         // Only re-ranking 6 entries so this should stay in most recent position.
         ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
         processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 6 ordered by largest pss, then last processes position unchanged.
         assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
-                rss16k);
+                rss16k).inOrder();
     }
 
     @Test
+    public void reRankLruCachedApps_rssImpactsOrdering_cachedRssValues()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 10000000,
+                /* usesWeight= */ 0.0f,
+                /* pssWeight= */ 1.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(rss10k);
+        ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(rss20k);
+        ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
+        processList.add(rss1k);
+        ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(rss100k);
+        ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(rss2k);
+        ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+        processList.add(rss15k);
+        // Only re-ranking 6 entries so this should stay in most recent position.
+        ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+        processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // First 6 ordered by largest pss, then last processes position unchanged.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+
+        // Clear mPidToRss so that Process.getRss calls fail.
+        mPidToRss.clear();
+        // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+        Collections.swap(processList, 0, 1);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // Re ranking is the same.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_rssImpactsOrdering_profileRss()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ false,
+                /* rssUpdateRateMs= */ 10000000,
+                /* usesWeight= */ 0.0f,
+                /* pssWeight= */ 1.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 0L, 1000);
+        rss10k.mProfile.setLastRss(10 * 1024L);
+        processList.add(rss10k);
+        ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 0L, 2000);
+        rss20k.mProfile.setLastRss(20 * 1024L);
+        processList.add(rss20k);
+        ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10000);
+        rss1k.mProfile.setLastRss(1024L);
+        processList.add(rss1k);
+        ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 0L, 10);
+        rss100k.mProfile.setLastRss(100 * 1024L);
+        processList.add(rss100k);
+        ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 0L, 20);
+        rss2k.mProfile.setLastRss(2 * 1024L);
+        processList.add(rss2k);
+        ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
+        rss15k.mProfile.setLastRss(15 * 1024L);
+        processList.add(rss15k);
+        // Only re-ranking 6 entries so this should stay in most recent position.
+        ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
+        rss16k.mProfile.setLastRss(16 * 1024L);
+        processList.add(rss16k);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        // This should not be used, as RSS values are taken from mProfile.
+        mPidToRss.clear();
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // First 6 ordered by largest pss, then last processes position unchanged.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+
+        // Clear mPidToRss so that Process.getRss calls fail.
+        mPidToRss.clear();
+        // Mix up the process list to ensure that CacheOomRanker actually re-ranks.
+        Collections.swap(processList, 0, 1);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+        // Re ranking is the same.
+        assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
+                rss16k).inOrder();
+    }
+
+
+    @Test
     public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.setLruProcessServiceStartLSP(1);
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(used1000);
         ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(used2000);
         ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(used10);
         ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
         processList.add(used20);
         // Only re-ranking 6 entries so last two should stay in most recent position.
         ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
         processList.add(used500);
         ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
         processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
         // First 4 ordered by uses, then last processes position unchanged.
         assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
-                used200);
+                used200).inOrder();
     }
 
     @Test
-    public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
+    public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
         setConfig(/* numberToReRank= */ 4,
-                /* usesWeight= */ 0.5f,
-                /* pssWeight= */ 0.2f,
-                /* lruWeight= */ 0.3f);
-
-        ProcessList list = new ProcessList();
-        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
-        ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
-        processList.add(unknownAdj1);
-        ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
-        processList.add(unknownAdj2);
-        ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
-        processList.add(unknownAdj3);
-        ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
-        processList.add(foregroundAdj);
-        ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
-        processList.add(serviceAdj);
-        ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
-        processList.add(systemAdj);
-
-        // 6 Processes but only 3 in eligible for cache so no re-ranking.
-        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
-
-        // All positions unchanged.
-        assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
-                foregroundAdj, serviceAdj, systemAdj);
-    }
-
-    @Test
-    public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
-        setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
                 /* usesWeight= */ 1.0f,
                 /* pssWeight= */ 0.0f,
                 /* lruWeight= */ 0.0f);
 
         ProcessList list = new ProcessList();
-        list.setLruProcessServiceStartLSP(4);
         ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
         ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
         processList.add(used1000);
         ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
         processList.add(used2000);
         ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
         processList.add(used10);
-        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
-        processList.add(used20);
-        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
-        processList.add(used500);
-        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
-                Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
-        processList.add(used200);
+        ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(foregroundAdj);
+        ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(serviceAdj);
+        ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(systemAdj);
+        list.setLruProcessServiceStartLSP(processList.size());
 
         mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
 
-        // All positions unchanged.
-        assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
-                used200);
+        // 6 processes, only 3 in eligible for cache, so only those are re-ranked.
+        assertThat(processList).containsExactly(used10, used1000, used2000,
+                foregroundAdj, serviceAdj, systemAdj).inOrder();
     }
 
-    private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
+    @Test
+    public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 4,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(service1);
+        ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(service2);
+        ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(service3);
+        list.setLruProcessServiceStartLSP(3);
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // Services unchanged, rest re-ranked.
+        assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
+                service3).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 0,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList set1List = new ProcessList();
+        ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
+        ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        set1ProcessList.add(set1Used1000);
+        ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        set1ProcessList.add(set1Used2000);
+        ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        set1ProcessList.add(set1Used10);
+        ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        set1ProcessList.add(set1Uses20);
+        ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        set1ProcessList.add(set1Uses500);
+        ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        set1ProcessList.add(set1Uses200);
+        set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
+                set1List.getLruProcessServiceStartLOSP());
+        assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
+                set1Uses500, set1Used1000, set1Used2000).inOrder();
+
+        ProcessList set2List = new ProcessList();
+        ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
+        ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        set2ProcessList.add(set2Used1000);
+        ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        set2ProcessList.add(set2Used2000);
+        ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        set2ProcessList.add(set2Used10);
+        ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        set2ProcessList.add(set2ForegroundAdj);
+        ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        set2ProcessList.add(set2ServiceAdj);
+        ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        set2ProcessList.add(set2SystemAdj);
+        set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
+                set2List.getLruProcessServiceStartLOSP());
+        assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
+                set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 3,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        // Preserving the top 3 processes, so these should not be re-ranked.
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // First 3 ordered by uses, then last processes position unchanged.
+        assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+                used200).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ 100,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // Nothing reordered, as we preserve the top 100 apps.
+        assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
+                used200).inOrder();
+    }
+
+    @Test
+    public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
+            throws InterruptedException {
+        setConfig(/* numberToReRank= */ 6,
+                /* preserveTopNApps= */ -100,
+                /* useFrequentRss= */ true,
+                /* rssUpdateRateMs= */ 0,
+                /* usesWeight= */ 1.0f,
+                /* pssWeight= */ 0.0f,
+                /* lruWeight= */ 0.0f);
+
+        ProcessList list = new ProcessList();
+        ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+        ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+        processList.add(used1000);
+        ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+        processList.add(used2000);
+        ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+        processList.add(used10);
+        // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
+        ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+        processList.add(used20);
+        ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+        processList.add(used500);
+        ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+                NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+        processList.add(used200);
+        list.setLruProcessServiceStartLSP(processList.size());
+
+        mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+        // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
+        assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+                used200).inOrder();
+    }
+
+    private void setConfig(int numberToReRank, int preserveTopNApps, boolean useFrequentRss,
+            long rssUpdateRateMs, float usesWeight, float pssWeight, float lruWeight)
             throws InterruptedException {
         mExecutor.init(4);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -355,6 +707,18 @@
                 Integer.toString(numberToReRank),
                 false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+                Integer.toString(preserveTopNApps),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_USE_FREQUENT_RSS,
+                Boolean.toString(useFrequentRss),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                CacheOomRanker.KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS,
+                Long.toString(rssUpdateRateMs),
+                false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
                 Float.toString(lruWeight),
                 false);
@@ -364,17 +728,19 @@
                 false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
-                Float.toString(useWeight),
+                Float.toString(usesWeight),
                 false);
         mExecutor.waitForLatch();
         assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
+        assertThat(mCacheOomRanker.mUseFrequentRss).isEqualTo(useFrequentRss);
+        assertThat(mCacheOomRanker.mRssUpdateRateMs).isEqualTo(rssUpdateRateMs);
         assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
-        assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
+        assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
         assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
     }
 
     private ProcessRecord nextProcessRecord(int setAdj, long lastActivityTime, long lastRss,
-            int returnedToCacheCount) {
+            int wentToForegroundCount) {
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = "a.package.name" + mNextPackageName++;
         ProcessRecord app = new ProcessRecord(mAms, ai, ai.packageName + ":process", mNextUid++);
@@ -382,14 +748,20 @@
         app.info.uid = mNextPackageUid++;
         // Exact value does not mater, it can be any state for which compaction is allowed.
         app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
-        app.mState.setSetAdj(setAdj);
+        app.mState.setCurAdj(setAdj);
         app.setLastActivityTime(lastActivityTime);
-        app.mProfile.setLastRss(lastRss);
+        mPidToRss.put(app.getPid(), lastRss);
         app.mState.setCached(false);
-        for (int i = 0; i < returnedToCacheCount; ++i) {
-            app.mState.setCached(false);
-            app.mState.setCached(true);
+        for (int i = 0; i < wentToForegroundCount; ++i) {
+            app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+            app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT);
         }
+        // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
+        // app is currently launching.
+        ProcessStatsService processStatsService = new ProcessStatsService(
+                mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
+                "procstats"));
+        app.makeActive(mock(IApplicationThread.class), processStatsService);
         return app;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index f703e2e..d0b2eda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -906,6 +906,21 @@
     }
 
     @Test
+    public void testProviderRequest_DelayedRequest_Remove() {
+        mProvider.setProviderLocation(createLocation(NAME, mRandom));
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = new LocationRequest.Builder(60000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+        mManager.unregisterLocationRequest(listener1);
+
+        mInjector.getAlarmHelper().incrementAlarmTime(60000);
+        assertThat(mProvider.getRequest().isActive()).isFalse();
+    }
+
+    @Test
     public void testProviderRequest_SpamRequesting() {
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
index 7a6110b..f17fa62 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
@@ -79,8 +79,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
 
@@ -97,8 +97,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
 
@@ -118,8 +118,8 @@
         mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
         mockAllowList(packageSetting2, null)
 
-        pms.sendPackagesSuspendedForUser(
-                packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
         verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
                 anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
 
@@ -133,6 +133,22 @@
         }
     }
 
+    @Test
+    @Throws(Exception::class)
+    fun sendPackagesSuspendModifiedForUser() {
+        pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+                packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+        verify(pms).sendPackageBroadcast(
+                eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+        assertThat(modifiedUids).asList().containsExactly(
+                packageSetting1.appId, packageSetting2.appId)
+    }
+
     private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
         this.put(TEST_USER_ID, uids)
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index b1b6e53..03eba9b 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -18,6 +18,9 @@
 
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
@@ -34,10 +37,9 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import static org.mockito.Mockito.mock;
-
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowProcessController;
 
@@ -181,8 +183,10 @@
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE);
 
-        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE});
         flushHandler();
 
         // The current committed and requests states do not change because the current state remains
@@ -190,9 +194,10 @@
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE);
 
         assertArrayEquals(callback.getLastNotifiedInfo().supportedStates,
-                new int[] { DEFAULT_DEVICE_STATE.getIdentifier() });
+                new int[]{DEFAULT_DEVICE_STATE.getIdentifier()});
     }
 
     @Test
@@ -207,9 +212,11 @@
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE);
 
-        mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
-                OTHER_DEVICE_STATE });
+        mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE});
         flushHandler();
 
         // The current committed and requests states do not change because the current state remains
@@ -217,6 +224,8 @@
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+        assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+                OTHER_DEVICE_STATE);
 
         // The callback wasn't notified about a change in supported states as the states have not
         // changed.
@@ -335,6 +344,7 @@
                 DEFAULT_DEVICE_STATE.getIdentifier());
     }
 
+    @FlakyTest(bugId = 200332057)
     @Test
     public void requestState_pendingStateAtRequest() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
@@ -466,6 +476,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
     }
 
+    @FlakyTest(bugId = 200332057)
     @Test
     public void requestState_becomesUnsupported() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 8279624..fbc1952 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -16,12 +16,17 @@
 
 package com.android.server.display;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY_GROUP;
+
 import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED;
 import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED;
 import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
 import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
 import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.eq;
@@ -55,6 +60,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Arrays;
+import java.util.Set;
 
 @SmallTest
 @Presubmit
@@ -123,14 +129,14 @@
         // add
         LogicalDisplay displayAdded = add(device);
         assertEquals(info(displayAdded).address, info(device).address);
-        assertEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+        assertEquals(DEFAULT_DISPLAY, id(displayAdded));
 
         // remove
         mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
         verify(mListenerMock).onLogicalDisplayEventLocked(
                 mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
         LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
-        assertEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+        assertEquals(DEFAULT_DISPLAY, id(displayRemoved));
         assertEquals(displayAdded, displayRemoved);
     }
 
@@ -155,11 +161,11 @@
 
         LogicalDisplay display1 = add(device1);
         assertEquals(info(display1).address, info(device1).address);
-        assertNotEquals(Display.DEFAULT_DISPLAY, id(display1));
+        assertNotEquals(DEFAULT_DISPLAY, id(display1));
 
         LogicalDisplay display2 = add(device2);
         assertEquals(info(display2).address, info(device2).address);
-        assertEquals(Display.DEFAULT_DISPLAY, id(display2));
+        assertEquals(DEFAULT_DISPLAY, id(display2));
     }
 
     @Test
@@ -171,12 +177,12 @@
 
         LogicalDisplay display1 = add(device1);
         assertEquals(info(display1).address, info(device1).address);
-        assertEquals(Display.DEFAULT_DISPLAY, id(display1));
+        assertEquals(DEFAULT_DISPLAY, id(display1));
 
         LogicalDisplay display2 = add(device2);
         assertEquals(info(display2).address, info(device2).address);
         // Despite the flags, we can only have one default display
-        assertNotEquals(Display.DEFAULT_DISPLAY, id(display2));
+        assertNotEquals(DEFAULT_DISPLAY, id(display2));
     }
 
     @Test
@@ -189,7 +195,67 @@
         int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
         assertEquals(3, ids.length);
         Arrays.sort(ids);
-        assertEquals(Display.DEFAULT_DISPLAY, ids[0]);
+        assertEquals(DEFAULT_DISPLAY, ids[0]);
+    }
+
+    @Test
+    public void testGetDisplayInfoForStateLocked_oneDisplayGroup_internalType() {
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 700, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+
+        Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+                DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+        assertThat(displayInfos.size()).isEqualTo(3);
+        for (DisplayInfo displayInfo : displayInfos) {
+            assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+            assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+            assertThat(displayInfo.logicalWidth).isAnyOf(600, 200, 700);
+            assertThat(displayInfo.logicalHeight).isEqualTo(800);
+        }
+    }
+
+    @Test
+    public void testGetDisplayInfoForStateLocked_oneDisplayGroup_differentTypes() {
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_EXTERNAL, 700, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+
+        Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+                DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+        assertThat(displayInfos.size()).isEqualTo(2);
+        for (DisplayInfo displayInfo : displayInfos) {
+            assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+            assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+            assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
+            assertThat(displayInfo.logicalHeight).isEqualTo(800);
+        }
+    }
+
+    @Test
+    public void testGetDisplayInfoForStateLocked_multipleDisplayGroups_defaultGroup() {
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+                DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+        add(createDisplayDevice(Display.TYPE_VIRTUAL, 700, 800,
+                DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP));
+
+        Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+                DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+        assertThat(displayInfos.size()).isEqualTo(2);
+        for (DisplayInfo displayInfo : displayInfos) {
+            assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+            assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+            assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
+            assertThat(displayInfo.logicalHeight).isEqualTo(800);
+        }
     }
 
     @Test
@@ -199,11 +265,11 @@
         LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
         LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
 
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
     }
 
@@ -218,11 +284,11 @@
                 DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
         LogicalDisplay display3 = add(device3);
 
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
-        assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertNotEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
 
         // Now switch it back to the default group by removing the flag and issuing an update
@@ -231,7 +297,7 @@
         mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
 
         // Verify the new group is correct.
-        assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+        assertEquals(DEFAULT_DISPLAY_GROUP,
                 mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
     }
 
@@ -287,14 +353,14 @@
         // add
         LogicalDisplay displayAdded = add(device);
         assertEquals(info(displayAdded).address, info(device).address);
-        assertNotEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+        assertNotEquals(DEFAULT_DISPLAY, id(displayAdded));
 
         // remove
         mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
         verify(mListenerMock).onLogicalDisplayEventLocked(
                 mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
         LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
-        assertNotEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+        assertNotEquals(DEFAULT_DISPLAY, id(displayRemoved));
     }
 
     /**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index f9663f2..987236c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -65,6 +65,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -1320,16 +1321,15 @@
                 APPROVAL_BY_COMPONENT);
         ComponentName cn = ComponentName.unflattenFromString("a/a");
 
-        service.registerSystemService(cn, 0);
-        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
-            Object[] args = invocation.getArguments();
-            ServiceConnection sc = (ServiceConnection) args[1];
-            sc.onNullBinding(cn);
-            return true;
-        });
+        ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+        when(context.bindServiceAsUser(any(), captor.capture(), anyInt(), any()))
+                .thenAnswer(invocation -> {
+                    captor.getValue().onNullBinding(cn);
+                    return true;
+                });
 
         service.registerSystemService(cn, 0);
-        assertFalse(service.isBound(cn, 0));
+        verify(context).unbindService(captor.getValue());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f660af0..1ae219d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4011,6 +4011,80 @@
     }
 
     @Test
+    public void testApplyAdjustmentsLogged() throws Exception {
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+        when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
+
+        // Set up notifications that will be adjusted
+        final NotificationRecord r1 = generateNotificationRecord(
+                mTestNotificationChannel, 1, null, true);
+        r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+        mService.addNotification(r1);
+        final NotificationRecord r2 = generateNotificationRecord(
+                mTestNotificationChannel, 2, null, true);
+        r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+        mService.addNotification(r2);
+
+        // Third notification that's NOT adjusted, just to make sure that doesn't get spuriously
+        // logged.
+        final NotificationRecord r3 = generateNotificationRecord(
+                mTestNotificationChannel, 3, null, true);
+        r3.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
+        mService.addNotification(r3);
+
+        List<Adjustment> adjustments = new ArrayList<>();
+
+        // Test an adjustment that's associated with a ranking change and one that's not
+        Bundle signals1 = new Bundle();
+        signals1.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_HIGH);
+        Adjustment adjustment1 = new Adjustment(
+                r1.getSbn().getPackageName(), r1.getKey(), signals1, "",
+                r1.getUser().getIdentifier());
+        adjustments.add(adjustment1);
+
+        // This one wouldn't trigger a ranking change, but should still trigger a log.
+        Bundle signals2 = new Bundle();
+        signals2.putFloat(Adjustment.KEY_RANKING_SCORE, -0.5f);
+        Adjustment adjustment2 = new Adjustment(
+                r2.getSbn().getPackageName(), r2.getKey(), signals2, "",
+                r2.getUser().getIdentifier());
+        adjustments.add(adjustment2);
+
+        mBinderService.applyAdjustmentsFromAssistant(null, adjustments);
+        verify(mRankingHandler, times(1)).requestSort();
+
+        // Actually apply the adjustments & recalculate importance when run
+        doAnswer(invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .applyAdjustments();
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .calculateImportance();
+            return null;
+        }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+
+        // Now make sure that when the sort happens, we actually log the changes.
+        mService.handleRankingSort();
+
+        // Even though the ranking score change is not meant to trigger a ranking update,
+        // during this process the package visibility & canShowBadge values are changing
+        // in all notifications, so all 3 seem to trigger a ranking change. Here we check instead
+        // that scheduleSendRankingUpdate is sent and that the relevant fields have been changed
+        // accordingly to confirm the adjustments happened to the 2 relevant notifications.
+        verify(handler, times(3)).scheduleSendRankingUpdate();
+        assertEquals(IMPORTANCE_HIGH, r1.getImportance());
+        assertTrue(r2.rankingScoreMatches(-0.5f));
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+                mNotificationRecordLogger.event(0));
+        assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
+                mNotificationRecordLogger.event(1));
+        assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
+        assertEquals(2, mNotificationRecordLogger.get(1).getInstanceId());
+    }
+
+    @Test
     public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addEnqueuedNotification(r);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 64fd19e..8a11798 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -45,6 +45,15 @@
             groupInstanceId = groupId;
         }
 
+        CallRecord(NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId) {
+            super(r, null);
+            this.position = position;
+            this.buzzBeepBlink = buzzBeepBlink;
+            wasLogged = true;
+            event = NotificationReportedEvent.NOTIFICATION_ADJUSTED;
+            groupInstanceId = groupId;
+        }
+
         CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
             super(r, null);
             wasLogged = true;
@@ -75,6 +84,12 @@
     }
 
     @Override
+    public void logNotificationAdjusted(NotificationRecord r, int position, int buzzBeepBlink,
+            InstanceId groupId) {
+        mCalls.add(new CallRecord(r, position, buzzBeepBlink, groupId));
+    }
+
+    @Override
     public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
         mCalls.add(new CallRecord(r, event));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index b4fbf5f..184ea52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -110,18 +110,20 @@
             @Override
             public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
                 try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
-                    t.setVisibility(leash, true /* visible */).apply();
+                    t.show(leash).apply();
                 }
                 int cookieIndex = -1;
                 if (trampoline.equals(taskInfo.baseActivity)) {
                     cookieIndex = 0;
                 } else if (main.equals(taskInfo.baseActivity)) {
                     cookieIndex = 1;
-                    mainLatch.countDown();
                 }
                 if (cookieIndex >= 0) {
                     appearedCookies[cookieIndex] = taskInfo.launchCookies.isEmpty()
                             ? null : taskInfo.launchCookies.get(0);
+                    if (cookieIndex == 1) {
+                        mainLatch.countDown();
+                    }
                 }
             }
         };
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 9a14e7d..c434b13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -83,7 +82,6 @@
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -2779,10 +2777,36 @@
 
         assertEquals(taskBounds, activity.getAnimationBounds(ROOT_TASK_CLIP_AFTER_ANIM));
         assertEquals(new Point(0, 0), animationPosition);
+    }
 
-        // ROOT_TASK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
-        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        assertEquals(rootTask.getBounds(), activity.getAnimationBounds(ROOT_TASK_CLIP_BEFORE_ANIM));
+    @Test
+    public void testTransitionAnimationBounds_returnTaskFragment() {
+        removeGlobalMinSizeRestriction();
+        final Task task = new TaskBuilder(mSupervisor).setCreateParentTask(true).build();
+        final Task rootTask = task.getRootTask();
+        final TaskFragment taskFragment = createTaskFragmentWithParentTask(task,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity = taskFragment.getTopNonFinishingActivity();
+        final Rect stackBounds = new Rect(0, 0, 1000, 600);
+        final Rect taskBounds = new Rect(100, 400, 600, 800);
+        final Rect taskFragmentBounds = new Rect(100, 400, 300, 800);
+        final Rect activityBounds = new Rect(100, 400, 300, 600);
+        // Set the bounds and windowing mode to window configuration directly, otherwise the
+        // testing setups may be discarded by configuration resolving.
+        rootTask.getWindowConfiguration().setBounds(stackBounds);
+        task.getWindowConfiguration().setBounds(taskBounds);
+        taskFragment.getWindowConfiguration().setBounds(taskFragmentBounds);
+        activity.getWindowConfiguration().setBounds(activityBounds);
+
+        // Check that anim bounds for freeform window match task fragment bounds
+        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
+        assertEquals(taskFragment.getBounds(), activity.getAnimationBounds(ROOT_TASK_CLIP_NONE));
+
+        // ROOT_TASK_CLIP_AFTER_ANIM should use task fragment bounds since they will be clipped by
+        // bounds animation layer.
+        task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        assertEquals(taskFragment.getBounds(),
+                activity.getAnimationBounds(ROOT_TASK_CLIP_AFTER_ANIM));
     }
 
     @Test
@@ -2963,6 +2987,7 @@
         mDisplayContent.setImeInputTarget(app);
 
         // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        mDisplayContent.mOpeningApps.clear();
         app.mActivityRecord.commitVisibility(false, false);
         app.mActivityRecord.onWindowsGone();
 
@@ -2981,6 +3006,24 @@
         assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
     }
 
+    @Test
+    public void testInClosingAnimation_doNotHideSurface() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        makeWindowVisibleAndDrawn(app);
+
+        // Put the activity in close transition.
+        mDisplayContent.mOpeningApps.clear();
+        mDisplayContent.mClosingApps.add(app.mActivityRecord);
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+
+        // Update visibility and call to remove window
+        app.mActivityRecord.commitVisibility(false, false);
+        app.mActivityRecord.prepareSurfaces();
+
+        // Because the app is waiting for transition, it should not hide the surface.
+        assertTrue(app.mActivityRecord.isSurfaceShowing());
+    }
+
     private void assertHasStartingWindow(ActivityRecord atoken) {
         assertNotNull(atoken.mStartingSurface);
         assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0d0cec7..d6d7f07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -31,13 +31,17 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.os.Binder;
@@ -791,4 +795,38 @@
         verify(mDisplayContent.mAppTransition)
                 .overridePendingAppTransitionRemote(adapter, false /* sync */);
     }
+
+    @Test
+    public void testTransitionGoodToGoForTaskFragments() {
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final Task task = createTask(mDisplayContent);
+        final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(1)
+                .setOrganizer(organizer)
+                .build();
+        final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .setOrganizer(organizer)
+                .build();
+        changeTaskFragment.getTopMostActivity().allDrawn = true;
+        mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
+        mDisplayContent.mChangingContainers.add(changeTaskFragment);
+        spyOn(mDisplayContent.mAppTransition);
+        spyOn(emptyTaskFragment);
+
+        mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+        // Transition not ready because there is an empty non-finishing TaskFragment.
+        verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());
+
+        doReturn(true).when(emptyTaskFragment).hasChild();
+        emptyTaskFragment.remove(false /* withTransition */, "test");
+
+        mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+        // Transition ready because the empty (no running activity) TaskFragment is requested to be
+        // removed.
+        verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
+    }
 }
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index a0a3ce7..405d714 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -43,6 +43,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -54,6 +55,8 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
 
 import androidx.test.filters.SmallTest;
 
@@ -391,6 +394,35 @@
                 mDc.mAppTransition.getAnimationStyleResId(attrs));
     }
 
+    @Test
+    public void testActivityRecordReparentToTaskFragment() {
+        final ActivityRecord activity = createActivityRecord(mDc);
+        activity.setVisibility(true);
+        final Task task = activity.getTask();
+
+        // Add a TaskFragment of half of the Task size.
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final ITaskFragmentOrganizer iOrganizer =
+                ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+        mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .setOrganizer(organizer)
+                .build();
+        final Rect taskBounds = new Rect();
+        task.getBounds(taskBounds);
+        taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
+
+        assertTrue(mDc.mChangingContainers.isEmpty());
+        assertFalse(mDc.mAppTransition.isTransitionSet());
+
+        // Schedule app transition when reparent activity to a TaskFragment of different size.
+        activity.reparent(taskFragment, POSITION_TOP);
+
+        assertTrue(mDc.mChangingContainers.contains(activity));
+        assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE));
+    }
+
     private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         boolean mCancelled = false;
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7829362..e340217 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1572,6 +1572,12 @@
         mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
         assertTrue(displayRotation.updateRotationUnchecked(false));
 
+        // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep).
+        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
+        displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
+        ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false;
+        assertTrue(displayRotation.updateRotationUnchecked(false));
+
         // Rotation can be updated if the recents animation is animating but it is not on top, e.g.
         // switching activities in different orientations by quickstep gesture.
         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 223dc31..a1e8ca4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -58,6 +58,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -70,6 +71,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
@@ -97,14 +99,20 @@
     static class TestDragDropController extends DragDropController {
         private Runnable mCloseCallback;
         boolean mDeferDragStateClosed;
+        boolean mIsAccessibilityDrag;
 
         TestDragDropController(WindowManagerService service, Looper looper) {
             super(service, looper);
         }
 
         void setOnClosedCallbackLocked(Runnable runnable) {
-            assertTrue(dragDropActiveLocked());
-            mCloseCallback = runnable;
+            if (mIsAccessibilityDrag) {
+                // Accessibility does not use animation
+                assertTrue(!dragDropActiveLocked());
+            } else {
+                assertTrue(dragDropActiveLocked());
+                mCloseCallback = runnable;
+            }
         }
 
         @Override
@@ -171,6 +179,10 @@
         }
         latch = new CountDownLatch(1);
         mTarget.setOnClosedCallbackLocked(latch::countDown);
+        if (mTarget.mIsAccessibilityDrag) {
+            mTarget.mIsAccessibilityDrag = false;
+            return;
+        }
         assertTrue(awaitInWmLock(() -> latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)));
     }
 
@@ -180,6 +192,12 @@
     }
 
     @Test
+    public void testA11yDragFlow() {
+        mTarget.mIsAccessibilityDrag = true;
+        doA11yDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
+    }
+
+    @Test
     public void testPerformDrag_NullDataWithGrantUri() {
         doDragAndDrop(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0);
     }
@@ -436,4 +454,22 @@
             appSession.kill();
         }
     }
+
+    private void doA11yDragAndDrop(int flags, ClipData data, float dropX, float dropY) {
+        spyOn(mTarget);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        doReturn(accessibilityManager).when(mTarget).getAccessibilityManager();
+        startA11yDrag(flags, data, () -> {
+            boolean dropped = mTarget.dropForAccessibility(mWindow.mClient, dropX, dropY);
+            mToken = mWindow.mClient.asBinder();
+        });
+    }
+
+    private void startA11yDrag(int flags, ClipData data, Runnable r) {
+        mToken = mTarget.performDrag(0, 0, mWindow.mClient,
+                flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data);
+        assertNotNull(mToken);
+        r.run();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
new file mode 100644
index 0000000..6e00568
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRESENTATION;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+
+/**
+ * Tests for {@link PossibleDisplayInfoMapper}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PossibleDisplayInfoMapperTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
+
+    private PossibleDisplayInfoMapper mDisplayInfoMapper;
+    private final Set<DisplayInfo> mPossibleDisplayInfo = new ArraySet<>();
+    private DisplayInfo mDefaultDisplayInfo;
+    private DisplayInfo mSecondDisplayInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        mDisplayInfoMapper = mWm.mPossibleDisplayInfoMapper;
+        final DisplayInfo baseDisplayInfo = mWm.mRoot.getDisplayContent(
+                DEFAULT_DISPLAY).getDisplayInfo();
+        when(mWm.mDisplayManagerInternal.getPossibleDisplayInfo(anyInt())).thenReturn(
+                mPossibleDisplayInfo);
+
+        mDefaultDisplayInfo = new DisplayInfo(baseDisplayInfo);
+        initializeDisplayInfo(mDefaultDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 500, 800));
+        mSecondDisplayInfo = new DisplayInfo(baseDisplayInfo);
+        // Use the same display id for any display in the same group, due to the assumption that
+        // any display in the same grouped can be swapped out for each other (while maintaining the
+        // display id).
+        initializeDisplayInfo(mSecondDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 600, 1600));
+        mSecondDisplayInfo.flags |= FLAG_PRESENTATION;
+    }
+
+    @Test
+    public void testInitialization_isEmpty() {
+        // Empty after initializing.
+        assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
+
+        // Still empty after updating.
+        mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+        assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
+    }
+
+    @Test
+    public void testUpdatePossibleDisplayInfos_singleDisplay() {
+        mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+        mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+        Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+        // An entry for each possible rotation, for a display that can be in a single state.
+        assertThat(displayInfos.size()).isEqualTo(4);
+        assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
+    }
+
+    @Test
+    public void testUpdatePossibleDisplayInfos_secondDisplayAdded_sameGroup() {
+        mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+        mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+        assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+
+        // Add another display layout to the set of supported states.
+        mPossibleDisplayInfo.add(mSecondDisplayInfo);
+        mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+        Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+        Set<DisplayInfo> defaultDisplayInfos = new ArraySet<>();
+        Set<DisplayInfo> secondDisplayInfos = new ArraySet<>();
+        for (DisplayInfo di : displayInfos) {
+            if ((di.flags & FLAG_PRESENTATION) != 0) {
+                secondDisplayInfos.add(di);
+            } else {
+                defaultDisplayInfos.add(di);
+            }
+        }
+        // An entry for each possible rotation, for the default display.
+        assertThat(defaultDisplayInfos).hasSize(4);
+        assertPossibleDisplayInfoEntries(defaultDisplayInfos, mDefaultDisplayInfo);
+
+        // An entry for each possible rotation, for the second display.
+        assertThat(secondDisplayInfos).hasSize(4);
+        assertPossibleDisplayInfoEntries(secondDisplayInfos, mSecondDisplayInfo);
+    }
+
+    @Test
+    public void testUpdatePossibleDisplayInfos_secondDisplayAdded_differentGroup() {
+        mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+        mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+        assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+
+        // Add another display to a different group.
+        mSecondDisplayInfo.displayId = DEFAULT_DISPLAY + 1;
+        mSecondDisplayInfo.displayGroupId = mDefaultDisplayInfo.displayGroupId + 1;
+        mPossibleDisplayInfo.add(mSecondDisplayInfo);
+        mDisplayInfoMapper.updatePossibleDisplayInfos(mSecondDisplayInfo.displayId);
+
+        Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+        // An entry for each possible rotation, for the default display.
+        assertThat(displayInfos).hasSize(4);
+        assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
+
+        Set<DisplayInfo> secondStateEntries =
+                mDisplayInfoMapper.getPossibleDisplayInfos(mSecondDisplayInfo.displayId);
+        // An entry for each possible rotation, for the second display.
+        assertThat(secondStateEntries).hasSize(4);
+        assertPossibleDisplayInfoEntries(secondStateEntries, mSecondDisplayInfo);
+    }
+
+    private static void initializeDisplayInfo(DisplayInfo outDisplayInfo, int displayId,
+            Rect logicalBounds) {
+        outDisplayInfo.displayId = displayId;
+        outDisplayInfo.rotation = ROTATION_0;
+        outDisplayInfo.logicalWidth = logicalBounds.width();
+        outDisplayInfo.logicalHeight = logicalBounds.height();
+    }
+
+    private static void assertPossibleDisplayInfoEntries(Set<DisplayInfo> displayInfos,
+            DisplayInfo expectedDisplayInfo) {
+        boolean[] seenEveryRotation = new boolean[4];
+        for (DisplayInfo displayInfo : displayInfos) {
+            final int rotation = displayInfo.rotation;
+            seenEveryRotation[rotation] = true;
+            assertThat(displayInfo.displayId).isEqualTo(expectedDisplayInfo.displayId);
+            assertEqualsRotatedDisplayInfo(displayInfo, expectedDisplayInfo);
+        }
+        assertThat(seenEveryRotation).isEqualTo(new boolean[]{true, true, true, true});
+    }
+
+    private static void assertEqualsRotatedDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+        if (actual.rotation == ROTATION_0 || actual.rotation == ROTATION_180) {
+            assertThat(actual.logicalWidth).isEqualTo(expected.logicalWidth);
+            assertThat(actual.logicalHeight).isEqualTo(expected.logicalHeight);
+        } else {
+            assertThat(actual.logicalWidth).isEqualTo(expected.logicalHeight);
+            assertThat(actual.logicalHeight).isEqualTo(expected.logicalWidth);
+        }
+    }
+}
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 348472a..6407c92 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1171,7 +1171,6 @@
         final WindowState w = addWindowToActivity(mActivity);
         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
         mActivity.mRootWindowContainer.performSurfacePlacement();
-        mActivity.layoutLetterbox(null);
         // The letterbox insets should be [450, 0 - 250, 0].
         assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
                 mActivity.getLetterboxInsets());
@@ -1833,9 +1832,6 @@
     }
 
     private void assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(Rect parentBounds) {
-        // Ensure Letterbox is updated.
-        mActivity.layoutLetterbox(null);
-
         // Letterbox should fill the gap between the parent bounds and the letterboxed activity.
         final Rect letterboxedBounds = new Rect(mActivity.getBounds());
         assertTrue(parentBounds.contains(letterboxedBounds));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 98c1eabe..d475c46e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -76,7 +76,7 @@
 
     @Before
     public void setup() {
-        mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+        mController = mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController;
         mOrganizer = new TaskFragmentOrganizer(Runnable::run);
         mOrganizerToken = mOrganizer.getOrganizerToken();
         mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
@@ -284,7 +284,9 @@
     @Test
     public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()
             throws RemoteException {
+        mController.registerOrganizer(mIOrganizer);
         mOrganizer.applyTransaction(mTransaction);
+        doReturn(true).when(mTaskFragment).isAttached();
 
         // Throw exception if the transaction is trying to change a window that is not organized by
         // the organizer.
@@ -300,8 +302,18 @@
 
         // Allow transaction to change a TaskFragment created by the organizer.
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+        clearInvocations(mAtm.mRootWindowContainer);
 
         mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+        // No lifecycle update when the TaskFragment is not recorded.
+        verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities();
+
+        mAtm.mWindowOrganizerController.mLaunchTaskFragments
+                .put(mFragmentToken, mTaskFragment);
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
     }
 
     @Test
@@ -327,8 +339,11 @@
         // Allow transaction to change a TaskFragment created by the organizer.
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
         taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+        clearInvocations(mAtm.mRootWindowContainer);
 
         mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
     }
 
     @Test
@@ -360,6 +375,8 @@
     public void testApplyTransaction_enforceHierarchyChange_reparentChildren()
             throws RemoteException {
         mOrganizer.applyTransaction(mTransaction);
+        mController.registerOrganizer(mIOrganizer);
+        doReturn(true).when(mTaskFragment).isAttached();
 
         // Throw exception if the transaction is trying to change a window that is not organized by
         // the organizer.
@@ -375,7 +392,30 @@
 
         // Allow transaction to change a TaskFragment created by the organizer.
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+        clearInvocations(mAtm.mRootWindowContainer);
 
         mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
+    }
+
+    @Test
+    public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate()
+            throws RemoteException {
+        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+        mOrganizer.applyTransaction(mTransaction);
+        mController.registerOrganizer(mIOrganizer);
+        mTaskFragment = new TaskFragmentBuilder(mAtm)
+                .setCreateParentTask()
+                .setFragmentToken(mFragmentToken)
+                .build();
+        mAtm.mWindowOrganizerController.mLaunchTaskFragments
+                .put(mFragmentToken, mTaskFragment);
+        mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.appToken);
+        clearInvocations(mAtm.mRootWindowContainer);
+
+        mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+
+        verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
index e970c2a..e2f1334 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -19,7 +19,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -89,43 +88,6 @@
     }
 
     @Test
-    public void testApply_clipBeforeNoAnimationBounds() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, ROOT_TASK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */, 0 /* windowCornerRadius */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(mStackBounds)));
-    }
-
-    @Test
-    public void testApply_clipBeforeNoStackBounds() {
-        // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20)
-        Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        a.initialize(0, 0, 0, 0);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                null, false /* canSkipFirstFrame */, ROOT_TASK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */, 0 /* windowCornerRadius */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
-    }
-
-    @Test
-    public void testApply_setCornerRadius() {
-        final float windowCornerRadius = 30f;
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, ROOT_TASK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */, windowCornerRadius);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction, never()).setCornerRadius(eq(mSurfaceControl), eq(windowCornerRadius));
-        when(mAnimation.hasRoundedCorners()).thenReturn(true);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setCornerRadius(eq(mSurfaceControl), eq(windowCornerRadius));
-    }
-
-    @Test
     public void testApply_setCornerRadius_noClip() {
         final float windowCornerRadius = 30f;
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
@@ -136,32 +98,6 @@
         verify(mTransaction, never()).setCornerRadius(any(), anyFloat());
     }
 
-    @Test
-    public void testApply_clipBeforeSmallerAnimationClip() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5)
-        Rect windowCrop = new Rect(0, 0, 5, 5);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, ROOT_TASK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */, 0 /* windowCornerRadius */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(windowCrop)));
-    }
-
-    @Test
-    public void testApply_clipBeforeSmallerStackClip() {
-        // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20)
-        Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = createClipRectAnimation(windowCrop, windowCrop);
-        WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, ROOT_TASK_CLIP_BEFORE_ANIM,
-                true /* isAppAnimation */, 0 /* windowCornerRadius */);
-        windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
-        verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
-                argThat(rect -> rect.equals(mStackBounds)));
-    }
-
     private Animation createClipRectAnimation(Rect fromClip, Rect toClip) {
         Animation a = new ClipRectAnimation(fromClip, toClip);
         a.initialize(0, 0, 0, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1f6065f..6626aa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1140,6 +1140,7 @@
         private int mCreateActivityCount = 0;
         @Nullable
         private TaskFragmentOrganizer mOrganizer;
+        private IBinder mFragmentToken;
 
         TaskFragmentBuilder(ActivityTaskManagerService service) {
             mAtm = service;
@@ -1171,10 +1172,15 @@
             return this;
         }
 
+        TaskFragmentBuilder setFragmentToken(@Nullable IBinder fragmentToken) {
+            mFragmentToken = fragmentToken;
+            return this;
+        }
+
         TaskFragment build() {
             SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
 
-            final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
+            final TaskFragment taskFragment = new TaskFragment(mAtm, mFragmentToken,
                     mOrganizer != null);
             if (mParentTask == null && mCreateParentTask) {
                 mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp
index 702ddb1..af0eca9 100644
--- a/services/voiceinteraction/Android.bp
+++ b/services/voiceinteraction/Android.bp
@@ -18,5 +18,9 @@
     name: "services.voiceinteraction",
     defaults: ["platform_service_defaults"],
     srcs: [":services.voiceinteraction-sources"],
-    libs: ["services.core"],
+    libs: [
+        "services.core",
+        "android.hardware.power-V1-java",
+        "android.hardware.power-V1.0-java",
+    ],
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index cc021a9..08e9703 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -43,11 +43,13 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.graphics.Bitmap;
+import android.hardware.power.Boost;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -62,6 +64,7 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.am.AssistDataRequester;
 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
@@ -70,13 +73,20 @@
 import com.android.server.wm.ActivityAssistInfo;
 
 import java.io.PrintWriter;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 
 final class VoiceInteractionSessionConnection implements ServiceConnection,
         AssistDataRequesterCallbacks {
 
-    final static String TAG = "VoiceInteractionServiceManager";
+    static final String TAG = "VoiceInteractionServiceManager";
+    static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
+            System.getProperty("vendor.powerhal.interaction.max", "200"));
+    static final int BOOST_TIMEOUT_MS = 300;
+    // TODO: To avoid ap doesn't call hide, only 10 secs for now, need a better way to manage it
+    //  in the future.
+    static final int MAX_POWER_BOOST_TIMEOUT = 10_000;
 
     final IBinder mToken = new Binder();
     final Object mLock;
@@ -104,6 +114,45 @@
     ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
     private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
     AssistDataRequester mAssistDataRequester;
+    private final PowerManagerInternal mPowerManagerInternal;
+    private PowerBoostSetter mSetPowerBoostRunnable;
+    private final Handler mFgHandler;
+
+    class PowerBoostSetter implements Runnable {
+
+        private boolean mCanceled;
+        private final Instant mExpiryTime;
+
+        PowerBoostSetter(Instant expiryTime) {
+            mExpiryTime = expiryTime;
+        }
+
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (mCanceled) {
+                    return;
+                }
+                // To avoid voice interaction service does not call hide to cancel setting
+                // power boost. We will cancel set boost when reaching the max timeout.
+                if (Instant.now().isBefore(mExpiryTime)) {
+                    mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, BOOST_TIMEOUT_MS);
+                    if (mSetPowerBoostRunnable != null) {
+                        mFgHandler.postDelayed(mSetPowerBoostRunnable, POWER_BOOST_TIMEOUT_MS);
+                    }
+                } else {
+                    Slog.w(TAG, "Reset power boost INTERACTION because reaching max timeout.");
+                    mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
+                }
+            }
+        }
+
+        void cancel() {
+            synchronized (mLock) {
+                mCanceled =  true;
+            }
+        }
+    }
 
     IVoiceInteractionSessionShowCallback mShowCallback =
             new IVoiceInteractionSessionShowCallback.Stub() {
@@ -152,7 +201,9 @@
         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
+        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mFgHandler = FgThread.getHandler();
         mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
                 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
                 this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
@@ -255,6 +306,13 @@
                     mPendingHandleAssistWithoutData = topActivities;
                 }
             }
+            // remove if already existing one.
+            if (mSetPowerBoostRunnable != null) {
+                mSetPowerBoostRunnable.cancel();
+            }
+            mSetPowerBoostRunnable = new PowerBoostSetter(
+                    Instant.now().plusMillis(MAX_POWER_BOOST_TIMEOUT));
+            mFgHandler.post(mSetPowerBoostRunnable);
             mCallback.onSessionShown(this);
             return true;
         }
@@ -420,6 +478,12 @@
                     } catch (RemoteException e) {
                     }
                 }
+                if (mSetPowerBoostRunnable != null) {
+                    mSetPowerBoostRunnable.cancel();
+                    mSetPowerBoostRunnable = null;
+                }
+                // A negative value indicates canceling previous boost.
+                mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, /* durationMs */ -1);
                 mCallback.onSessionHidden(this);
             }
             if (mFullyBound) {
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index fe7e5976..41e24dd 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -26,8 +26,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -101,9 +103,11 @@
             }
 
             mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos);
-            // Sort the collection with RAN ascending order, make the ordering not matter for equals
+            // Sort the collection with RAN and then SignalMeasurementType ascending order, make the
+            // ordering not matter for equals
             mSignalThresholdInfos.sort(
-                    Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType));
+                    Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)
+                            .thenComparing(SignalThresholdInfo::getSignalMeasurementType));
             return this;
         }
 
@@ -144,7 +148,7 @@
          * @return the SignalStrengthUpdateRequest object
          *
          * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the
-         * radio access network type in the collection is not unique
+         * signal measurement type for the same RAN in the collection is not unique
          */
         public @NonNull SignalStrengthUpdateRequest build() {
             return new SignalStrengthUpdateRequest(mSignalThresholdInfos,
@@ -258,14 +262,23 @@
     }
 
     /**
-     * Throw IAE when the RAN in the collection is not unique.
+     * Throw IAE if SignalThresholdInfo collection is null or empty,
+     * or the SignalMeasurementType for the same RAN in the collection is not unique.
      */
     private static void validate(Collection<SignalThresholdInfo> infos) {
-        Set<Integer> uniqueRan = new HashSet<>(infos.size());
+        if (infos == null || infos.isEmpty()) {
+            throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
+        }
+
+        // Map from RAN to set of SignalMeasurementTypes
+        Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size());
         for (SignalThresholdInfo info : infos) {
             final int ran = info.getRadioAccessNetworkType();
-            if (!uniqueRan.add(ran)) {
-                throw new IllegalArgumentException("RAN: " + ran + " is not unique");
+            final int type = info.getSignalMeasurementType();
+            ranToTypes.putIfAbsent(ran, new HashSet<>());
+            if (!ranToTypes.get(ran).add(type)) {
+                throw new IllegalArgumentException(
+                        "SignalMeasurementType " + type + " for RAN " + ran + " is not unique");
             }
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d5be4f36..9896634 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8854,7 +8854,8 @@
     }
 
     /**
-     * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+     * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+     * and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
@@ -8865,7 +8866,8 @@
     }
 
     /**
-     * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
+     * Set the preferred network type to global mode which includes NR, LTE, CDMA, EvDo
+     * and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
@@ -8873,7 +8875,7 @@
      * @hide
      */
     public boolean setPreferredNetworkTypeToGlobal(int subId) {
-        return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+        return setPreferredNetworkType(subId, RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
     }
 
     /**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index fe1b1cd..b37c404 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.content.ComponentName
+import android.os.SystemProperties
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
@@ -43,6 +44,7 @@
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,6 +63,8 @@
 class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+    private val isShellTransitionsEnabled =
+            SystemProperties.getBoolean("persist.debug.shell_transit", false)
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -122,15 +126,17 @@
 
     @Presubmit
     @Test
-    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(true)
+    fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
 
     @Presubmit
     @Test
-    fun imeAppWindowVisibility() {
-        // the app starts visible in live tile, then becomes invisible during animation and
-        // is again launched. Since we log 1x per frame, sometimes the activity visibility and
-        // the app visibility are updated together, sometimes not, thus ignore activity check
-        // at the start
+    fun imeAppWindowVisibilityLegacy() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        // the app starts visible in live tile, and stays visible for the duration of entering
+        // and exiting overview. However, legacy transitions seem to have a bug which causes
+        // everything to restart during the test, so expect the app to disappear and come back.
+        // Since we log 1x per frame, sometimes the activity visibility and the app visibility
+        // are updated together, sometimes not, thus ignore activity check at the start
         testSpec.assertWm {
             this.isAppWindowVisible(testApp.component, ignoreActivity = true)
                     .then()
@@ -142,6 +148,19 @@
 
     @Presubmit
     @Test
+    fun imeAppWindowVisibility() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        // the app starts visible in live tile, and stays visible for the duration of entering
+        // and exiting overview. Since we log 1x per frame, sometimes the activity visibility
+        // and the app visibility are updated together, sometimes not, thus ignore activity
+        // check at the start
+        testSpec.assertWm {
+            this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+        }
+    }
+
+    @Presubmit
+    @Test
     // During testing the launcher is always in portrait mode
     fun entireScreenCovered() = testSpec.entireScreenCovered()
 
@@ -155,7 +174,8 @@
 
     @Presubmit
     @Test
-    fun imeLayerIsBecomesVisible() {
+    fun imeLayerIsBecomesVisibleLegacy() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
         testSpec.assertLayers {
             this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
                     .then()
@@ -167,6 +187,15 @@
 
     @Presubmit
     @Test
+    fun imeLayerIsBecomesVisible() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        testSpec.assertLayers {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
     fun appLayerReplacesLauncher() {
         testSpec.assertLayers {
             this.isVisible(LAUNCHER_COMPONENT)
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 0f84f6e..c9a8947a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -322,6 +322,7 @@
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
         verify(mSafeModeTimeoutAlarm).cancel();
         assertFalse(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
     }
 
     @Test
@@ -391,6 +392,7 @@
 
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
 
+        verifySafeModeStateAndCallbackFired(2 /* invocationCount */, false /* isInSafeMode */);
         assertFalse(mGatewayConnection.isInSafeMode());
     }
 
@@ -400,7 +402,7 @@
         mTestLooper.dispatchAll();
 
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
-        assertFalse(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
 
         // Trigger a failed validation, and the subsequent safemode timeout.
         triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
@@ -416,7 +418,7 @@
         runnableCaptor.getValue().run();
         mTestLooper.dispatchAll();
 
-        assertTrue(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
     }
 
     private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a696b3a..64d0bca 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -23,7 +23,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -301,6 +300,11 @@
                 expectCanceled);
     }
 
+    protected void verifySafeModeStateAndCallbackFired(int invocationCount, boolean isInSafeMode) {
+        verify(mGatewayStatusCallback, times(invocationCount)).onSafeModeStatusChanged();
+        assertEquals(isInSafeMode, mGatewayConnection.isInSafeMode());
+    }
+
     protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
             @NonNull State expectedState) {
         // Set a VcnNetworkAgent, and expect it to be unregistered and cleared
@@ -314,9 +318,8 @@
         delayedEvent.run();
         mTestLooper.dispatchAll();
 
-        verify(mGatewayStatusCallback).onSafeModeStatusChanged();
         assertEquals(expectedState, mGatewayConnection.getCurrentState());
-        assertTrue(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1, true);
 
         verify(mockNetworkAgent).unregister();
         assertNull(mGatewayConnection.getNetworkAgent());