Merge "Throw when setting RLP with privileged routers" into main
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3baa92c..cda9ae3 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -119,6 +119,7 @@
     method @NonNull public android.os.IBinder getProcessToken();
     method @NonNull public android.os.UserHandle getUser();
     field public static final String PAC_PROXY_SERVICE = "pac_proxy";
+    field public static final String REMOTE_AUTH_SERVICE = "remote_auth";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 58c2548..a09d7dc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -203,6 +203,7 @@
 import android.window.SizeConfigurationBuckets;
 import android.window.SplashScreen;
 import android.window.SplashScreenView;
+import android.window.WindowContextInfo;
 import android.window.WindowProviderService;
 import android.window.WindowTokenClientController;
 
@@ -6248,10 +6249,9 @@
     }
 
     @Override
-    public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId) {
-        WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
-                configuration, displayId);
+    public void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info) {
+        WindowTokenClientController.getInstance().onWindowContextInfoChanged(clientToken, info);
     }
 
     @Override
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f7a43f4..6753cb8 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -28,6 +28,7 @@
 import android.util.MergedConfiguration;
 import android.view.SurfaceControl;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
+import android.window.WindowContextInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
@@ -163,9 +164,9 @@
     public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
             Configuration overrideConfig, int displayId);
 
-    /** Deliver {@link android.window.WindowContext} configuration change. */
-    public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId);
+    /** Deliver {@link android.window.WindowContextInfo} change. */
+    public abstract void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info);
 
     /** Deliver {@link android.window.WindowContext} window removal event. */
     public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
deleted file mode 100644
index 3ac642f..0000000
--- a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.servertransaction;
-
-import static android.view.Display.INVALID_DISPLAY;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ClientTransactionHandler;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.os.Parcel;
-
-import java.util.Objects;
-
-/**
- * {@link android.window.WindowContext} configuration change message.
- * @hide
- */
-public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
-
-    @Nullable
-    private IBinder mClientToken;
-    @Nullable
-    private Configuration mConfiguration;
-    private int mDisplayId;
-
-    @Override
-    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
-            @NonNull PendingTransactionActions pendingActions) {
-        client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
-    }
-
-    // ObjectPoolItem implementation
-
-    private WindowContextConfigurationChangeItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowContextConfigurationChangeItem obtain(
-            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
-        WindowContextConfigurationChangeItem instance =
-                ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
-        if (instance == null) {
-            instance = new WindowContextConfigurationChangeItem();
-        }
-        instance.mClientToken = requireNonNull(clientToken);
-        instance.mConfiguration = requireNonNull(config);
-        instance.mDisplayId = displayId;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mClientToken = null;
-        mConfiguration = null;
-        mDisplayId = INVALID_DISPLAY;
-        ObjectPool.recycle(this);
-    }
-
-    // Parcelable implementation
-
-    /** Writes to Parcel. */
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeStrongBinder(mClientToken);
-        dest.writeTypedObject(mConfiguration, flags);
-        dest.writeInt(mDisplayId);
-    }
-
-    /** Reads from Parcel. */
-    private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
-        mClientToken = in.readStrongBinder();
-        mConfiguration = in.readTypedObject(Configuration.CREATOR);
-        mDisplayId = in.readInt();
-    }
-
-    public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
-            new Creator<>() {
-                public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
-                    return new WindowContextConfigurationChangeItem(in);
-                }
-
-                public WindowContextConfigurationChangeItem[] newArray(int size) {
-                    return new WindowContextConfigurationChangeItem[size];
-                }
-    };
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
-        return Objects.equals(mClientToken, other.mClientToken)
-                && Objects.equals(mConfiguration, other.mConfiguration)
-                && mDisplayId == other.mDisplayId;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + Objects.hashCode(mClientToken);
-        result = 31 * result + Objects.hashCode(mConfiguration);
-        result = 31 * result + mDisplayId;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
-                + ", config=" + mConfiguration
-                + ", displayId=" + mDisplayId
-                + "}";
-    }
-}
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
new file mode 100644
index 0000000..74721d5
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.window.WindowContextInfo;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextInfoChangeItem extends ClientTransactionItem {
+
+    @Nullable
+    private IBinder mClientToken;
+    @Nullable
+    private WindowContextInfo mInfo;
+
+    @Override
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+            @NonNull PendingTransactionActions pendingActions) {
+        client.handleWindowContextInfoChanged(mClientToken, mInfo);
+    }
+
+    // ObjectPoolItem implementation
+
+    private WindowContextInfoChangeItem() {}
+
+    /** Obtains an instance initialized with provided params. */
+    public static WindowContextInfoChangeItem obtain(
+            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+        WindowContextInfoChangeItem instance =
+                ObjectPool.obtain(WindowContextInfoChangeItem.class);
+        if (instance == null) {
+            instance = new WindowContextInfoChangeItem();
+        }
+        instance.mClientToken = requireNonNull(clientToken);
+        instance.mInfo = new WindowContextInfo(config, displayId);
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        mClientToken = null;
+        mInfo = null;
+        ObjectPool.recycle(this);
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mClientToken);
+        dest.writeTypedObject(mInfo, flags);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextInfoChangeItem(@NonNull Parcel in) {
+        mClientToken = in.readStrongBinder();
+        mInfo = in.readTypedObject(WindowContextInfo.CREATOR);
+    }
+
+    public static final @NonNull Creator<WindowContextInfoChangeItem> CREATOR =
+            new Creator<>() {
+                public WindowContextInfoChangeItem createFromParcel(Parcel in) {
+                    return new WindowContextInfoChangeItem(in);
+                }
+
+                public WindowContextInfoChangeItem[] newArray(int size) {
+                    return new WindowContextInfoChangeItem[size];
+                }
+    };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextInfoChangeItem other = (WindowContextInfoChangeItem) o;
+        return Objects.equals(mClientToken, other.mClientToken)
+                && Objects.equals(mInfo, other.mInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mClientToken);
+        result = 31 * result + Objects.hashCode(mInfo);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextInfoChangeItem{clientToken=" + mClientToken
+                + ", info=" + mInfo
+                + "}";
+    }
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b2cd7e9..9253998 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1985,7 +1985,7 @@
      * Open a raw file descriptor to access (potentially type transformed)
      * data from a "content:" URI.  This interacts with the underlying
      * {@link ContentProvider#openTypedAssetFile} method of the provider
-     * associated with the given URI, to retrieve retrieve any appropriate
+     * associated with the given URI, to retrieve any appropriate
      * data stream for the data stored there.
      *
      * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2a6d84b..c11a8fc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6341,6 +6341,18 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.remoteauth.RemoteAuthManager} to discover,
+     * register and authenticate via remote authenticator  devices.
+     *
+     * @see #getSystemService(String)
+     * @see android.remoteauth.RemoteAuthManager
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String REMOTE_AUTH_SERVICE = "remote_auth";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.ambientcontext.AmbientContextManager}.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 3e0ab90..518744e 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.util.Pools.SimplePool;
+import android.util.Slog;
 
 import androidx.annotation.StyleableRes;
 
@@ -41,6 +42,7 @@
     public static final int MAX_ATTR_LEN_PATH = 4000;
     public static final int MAX_ATTR_LEN_DATA_VALUE = 4000;
 
+    private static final String TAG = "PackageParsing";
     protected static final String TAG_ACTION = "action";
     protected static final String TAG_ACTIVITY = "activity";
     protected static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
@@ -715,23 +717,126 @@
         mChildTagMask |= 1 << idx;
     }
 
+    private boolean isComponentNameAttr(String name) {
+        switch (mTag) {
+            case TAG_ACTIVITY:
+                switch (name) {
+                    case TAG_ATTR_NAME:
+                    case TAG_ATTR_PARENT_ACTIVITY_NAME:
+                        return true;
+                    default:
+                        return false;
+                }
+            case TAG_ACTIVITY_ALIAS:
+                switch (name) {
+                    case TAG_ATTR_TARGET_ACTIVITY:
+                        return true;
+                    default:
+                        return false;
+                }
+            case TAG_APPLICATION:
+                switch (name) {
+                    case TAG_ATTR_BACKUP_AGENT:
+                    case TAG_ATTR_NAME:
+                        return true;
+                    default:
+                        return false;
+                }
+            case TAG_INSTRUMENTATION:
+            case TAG_PROVIDER:
+            case TAG_RECEIVER:
+            case TAG_SERVICE:
+            case TAG_USES_LIBRARY:
+                switch (name) {
+                    case TAG_ATTR_NAME:
+                        return true;
+                    default:
+                        return false;
+                }
+            default:
+                return false;
+        }
+    }
+
+    private boolean isComponentNameAttr(@StyleableRes int index) {
+        switch (mTag) {
+            case TAG_ACTIVITY:
+                return index == R.styleable.AndroidManifestActivity_name
+                        || index == R.styleable.AndroidManifestActivity_parentActivityName;
+            case TAG_ACTIVITY_ALIAS:
+                return index == R.styleable.AndroidManifestActivityAlias_targetActivity;
+            case TAG_APPLICATION:
+                return index == R.styleable.AndroidManifestApplication_backupAgent
+                        || index == R.styleable.AndroidManifestApplication_name;
+            case TAG_INSTRUMENTATION:
+                return index ==  R.styleable.AndroidManifestInstrumentation_name;
+            case TAG_PROVIDER:
+                return index ==  R.styleable.AndroidManifestProvider_name;
+            case TAG_RECEIVER:
+                return index ==  R.styleable.AndroidManifestReceiver_name;
+            case TAG_SERVICE:
+                return index ==  R.styleable.AndroidManifestService_name;
+            case TAG_USES_LIBRARY:
+                return index ==  R.styleable.AndroidManifestUsesLibrary_name;
+            default:
+                return false;
+        }
+    }
+
     boolean hasChild(String tag) {
         return (mChildTagMask & (1 << getCounterIdx(tag))) != 0;
     }
 
+    void validateComponentName(CharSequence name) {
+        int i = 0;
+        if (name.charAt(0) == '.') {
+            i = 1;
+        }
+        boolean isStart = true;
+        for (; i < name.length(); i++) {
+            if (name.charAt(i) == '.') {
+                if (isStart) {
+                    break;
+                }
+                isStart = true;
+            } else {
+                if (isStart) {
+                    if (Character.isJavaIdentifierStart(name.charAt(i))) {
+                        isStart = false;
+                    } else {
+                        break;
+                    }
+                } else if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+                    break;
+                }
+            }
+        }
+        if ((i < name.length()) || (name.charAt(name.length() - 1) == '.')) {
+            Slog.e(TAG, name + " is not a valid Java class name");
+            throw new SecurityException(name + " is not a valid Java class name");
+        }
+    }
+
     void validateStrAttr(String attrName, String attrValue) {
         if (attrValue != null && attrValue.length() > getAttrStrMaxLen(attrName)) {
             throw new SecurityException("String length limit exceeded for attribute " + attrName
                     + " in " + mTag);
         }
+        if (isComponentNameAttr(attrName)) {
+            validateComponentName(attrValue);
+        }
     }
 
     void validateResStrAttr(@StyleableRes int index, CharSequence stringValue) {
         if (stringValue != null && stringValue.length() > getResStrMaxLen(index)) {
             throw new SecurityException("String length limit exceeded for attribute in " + mTag);
         }
+        if (isComponentNameAttr(index)) {
+            validateComponentName(stringValue);
+        }
     }
 
+
     void seen(@NonNull Element element) {
         TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
         if (counter != null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index ae700a0..e06699b 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -266,7 +266,7 @@
         List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
         ArrayList<OutputConfiguration> outputList = new ArrayList<>();
         for (CameraOutputConfig output : outputConfigs) {
-            Surface outputSurface = initializeSurfrace(output);
+            Surface outputSurface = initializeSurface(output);
             if (outputSurface == null) {
                 continue;
             }
@@ -279,7 +279,7 @@
             if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) {
                 cameraOutput.enableSurfaceSharing();
                 for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) {
-                    Surface sharedSurface = initializeSurfrace(sharedOutputConfig);
+                    Surface sharedSurface = initializeSurface(sharedOutputConfig);
                     if (sharedSurface == null) {
                         continue;
                     }
@@ -1159,7 +1159,7 @@
         return ret;
     }
 
-    private Surface initializeSurfrace(CameraOutputConfig output) {
+    private Surface initializeSurface(CameraOutputConfig output) {
         switch(output.type) {
             case CameraOutputConfig.TYPE_SURFACE:
                 if (output.surface == null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1db4808..5d25681 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -2110,10 +2110,10 @@
             HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap, Integer jpegOrientation,
             Byte jpegQuality) {
         ArrayList<CaptureBundle> ret = new ArrayList<>();
-        for (Integer stagetId : captureMap.keySet()) {
-            Pair<Image, TotalCaptureResult> entry = captureMap.get(stagetId);
+        for (Integer stageId : captureMap.keySet()) {
+            Pair<Image, TotalCaptureResult> entry = captureMap.get(stageId);
             CaptureBundle bundle = new CaptureBundle();
-            bundle.stage = stagetId;
+            bundle.stage = stageId;
             bundle.captureImage = initializeParcelImage(entry.first);
             bundle.sequenceId = entry.second.getSequenceId();
             bundle.captureResult = entry.second.getNativeMetadata();
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7b5dd55..795eb4a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1019,8 +1019,9 @@
             if (!mOnPreparedStylusHwCalled) {
                 // prepare hasn't been called by Stylus HOVER.
                 onPrepareStylusHandwriting();
-                mOnPreparedStylusHwCalled = true;
             }
+            // reset flag as it's not relevant after onStartStylusHandwriting().
+            mOnPreparedStylusHwCalled = false;
             if (onStartStylusHandwriting()) {
                 cancelStylusWindowIdleTimeout();
                 mPrivOps.onStylusHandwritingReady(requestId, Process.myPid());
@@ -3089,7 +3090,8 @@
         mInputStarted = false;
         mStartedInputConnection = null;
         mCurCompletions = null;
-        if (mInkWindow != null) {
+        if (!mOnPreparedStylusHwCalled) {
+            // If IME didn't prepare to show InkWindow for current handwriting session.
             finishStylusHandwriting();
         }
         // Back callback is typically unregistered in {@link #hideWindow()}, but it's possible
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index db1a741..f32a1f8 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -24,7 +24,9 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -151,6 +153,13 @@
     public abstract boolean hasVibrator(int vibratorId);
 
     /**
+     * Returns a compact version of the {@link #toString()} result for debugging purposes.
+     *
+     * @hide
+     */
+    public abstract String toDebugString();
+
+    /**
      * Adapts a {@link VibrationEffect} to a specific device vibrator using the ID.
      *
      * <p>This can be used for adapting effects to the capabilities of the specific device vibrator
@@ -437,6 +446,13 @@
             return "Mono{mEffect=" + mEffect + '}';
         }
 
+        /** @hide */
+        @Override
+        public String toDebugString() {
+            // Simplify vibration string, use the single effect to represent it.
+            return mEffect.toDebugString();
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -619,6 +635,17 @@
             return "Stereo{mEffects=" + mEffects + '}';
         }
 
+        /** @hide */
+        @Override
+        public String toDebugString() {
+            StringJoiner sj = new StringJoiner(",", "Stereo{", "}");
+            for (int i = 0; i < mEffects.size(); i++) {
+                sj.add(String.format(Locale.ROOT, "vibrator(id=%d): %s",
+                        mEffects.keyAt(i), mEffects.valueAt(i).toDebugString()));
+            }
+            return sj.toString();
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -833,6 +860,17 @@
             return "Sequential{mEffects=" + mEffects + ", mDelays=" + mDelays + '}';
         }
 
+        /** @hide */
+        @Override
+        public String toDebugString() {
+            StringJoiner sj = new StringJoiner(",", "Sequential{", "}");
+            for (int i = 0; i < mEffects.size(); i++) {
+                sj.add(String.format(Locale.ROOT, "delayMs=%d, effect=%s",
+                        mDelays.get(i), mEffects.get(i).toDebugString()));
+            }
+            return sj.toString();
+        }
+
         @Override
         public int describeContents() {
             return 0;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index b2d62eb..98f9dff 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -336,10 +336,11 @@
 
     @Override
     public String toString() {
-        return "VibrationAttributes:"
-                + " Usage=" + usageToString()
-                + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
-                + " Flags=" + mFlags;
+        return "VibrationAttributes{"
+                + "mUsage=" + usageToString()
+                + ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+                + ", mFlags=" + mFlags
+                + '}';
     }
 
     /** @hide */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c3d7540..b24b45d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -44,7 +44,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
+import java.util.StringJoiner;
 
 /**
  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -629,6 +631,13 @@
         return MathUtils.constrain(a * fx, 0f, 1f);
     }
 
+    /**
+     * Returns a compact version of the {@link #toString()} result for debugging purposes.
+     *
+     * @hide
+     */
+    public abstract String toDebugString();
+
     /** @hide */
     public static String effectIdToString(int effectId) {
         switch (effectId) {
@@ -925,6 +934,23 @@
                     + "}";
         }
 
+        /** @hide */
+        @Override
+        public String toDebugString() {
+            if (mSegments.size() == 1 && mRepeatIndex < 0) {
+                // Simplify effect string, use the single segment to represent it.
+                return mSegments.get(0).toDebugString();
+            }
+            StringJoiner sj = new StringJoiner(",", "[", "]");
+            for (int i = 0; i < mSegments.size(); i++) {
+                sj.add(mSegments.get(i).toDebugString());
+            }
+            if (mRepeatIndex >= 0) {
+                return String.format(Locale.ROOT, "%s, repeat=%d", sj, mRepeatIndex);
+            }
+            return sj.toString();
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -1250,23 +1276,23 @@
         public static String primitiveToString(@PrimitiveType int id) {
             switch (id) {
                 case PRIMITIVE_NOOP:
-                    return "PRIMITIVE_NOOP";
+                    return "NOOP";
                 case PRIMITIVE_CLICK:
-                    return "PRIMITIVE_CLICK";
+                    return "CLICK";
                 case PRIMITIVE_THUD:
-                    return "PRIMITIVE_THUD";
+                    return "THUD";
                 case PRIMITIVE_SPIN:
-                    return "PRIMITIVE_SPIN";
+                    return "SPIN";
                 case PRIMITIVE_QUICK_RISE:
-                    return "PRIMITIVE_QUICK_RISE";
+                    return "QUICK_RISE";
                 case PRIMITIVE_SLOW_RISE:
-                    return "PRIMITIVE_SLOW_RISE";
+                    return "SLOW_RISE";
                 case PRIMITIVE_QUICK_FALL:
-                    return "PRIMITIVE_QUICK_FALL";
+                    return "QUICK_FALL";
                 case PRIMITIVE_TICK:
-                    return "PRIMITIVE_TICK";
+                    return "TICK";
                 case PRIMITIVE_LOW_TICK:
-                    return "PRIMITIVE_LOW_TICK";
+                    return "LOW_TICK";
                 default:
                     return Integer.toString(id);
             }
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 71ec096..02e6856 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
+import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.util.Range;
 import android.util.SparseBooleanArray;
@@ -207,6 +208,25 @@
                 + '}';
     }
 
+    /** @hide */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("VibratorInfo:");
+        pw.increaseIndent();
+        pw.println("id = " + mId);
+        pw.println("capabilities = " + Arrays.toString(getCapabilitiesNames()));
+        pw.println("capabilitiesFlags = " + Long.toBinaryString(mCapabilities));
+        pw.println("supportedEffects = " + Arrays.toString(getSupportedEffectsNames()));
+        pw.println("supportedPrimitives = " + Arrays.toString(getSupportedPrimitivesNames()));
+        pw.println("supportedBraking = " + Arrays.toString(getSupportedBrakingNames()));
+        pw.println("primitiveDelayMax = " + mPrimitiveDelayMax);
+        pw.println("compositionSizeMax = " + mCompositionSizeMax);
+        pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax);
+        pw.println("pwleSizeMax = " + mPwleSizeMax);
+        pw.println("q-factor = " + mQFactor);
+        pw.println("frequencyProfile = " + mFrequencyProfile);
+        pw.decreaseIndent();
+    }
+
     /** Return the id of this vibrator. */
     public int getId() {
         return mId;
@@ -370,7 +390,7 @@
      * Gets the resonant frequency of the vibrator.
      *
      * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
-     *         this vibrator is a composite of multiple physical devices.
+     * this vibrator is a composite of multiple physical devices.
      */
     public float getResonantFrequencyHz() {
         return mFrequencyProfile.mResonantFrequencyHz;
@@ -380,7 +400,7 @@
      * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
      *
      * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
-     *         this vibrator is a composite of multiple physical devices.
+     * this vibrator is a composite of multiple physical devices.
      */
     public float getQFactor() {
         return mQFactor;
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 462e5ad..42b6c2da 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -203,6 +203,15 @@
                 + "}";
     }
 
+    /** @hide */
+    @Override
+    public String toDebugString() {
+        return String.format("Prebaked=%s(%s, %s fallback)",
+                VibrationEffect.effectIdToString(mEffectId),
+                VibrationEffect.effectStrengthToString(mEffectStrength),
+                mFallback ? "with" : "no");
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 815a1dc..c52a09c 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -140,6 +140,13 @@
                 + '}';
     }
 
+    /** @hide */
+    @Override
+    public String toDebugString() {
+        return String.format("Primitive=%s(scale=%.2f, delay=%dms)",
+                VibrationEffect.Composition.primitiveToString(mPrimitiveId), mScale, mDelay);
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (this == o) return true;
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index a5dee6c..e997bcd 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -179,6 +179,17 @@
                 + "}";
     }
 
+    /** @hide */
+    @Override
+    public String toDebugString() {
+        return String.format("Ramp=%dms(amplitude=%.2f%s to %.2f%s)",
+                mDuration,
+                mStartAmplitude,
+                Float.compare(mStartFrequencyHz, 0) == 0 ? "" : " @ " + mStartFrequencyHz + "Hz",
+                mEndAmplitude,
+                Float.compare(mEndFrequencyHz, 0) == 0 ? "" : " @ " + mEndFrequencyHz + "Hz");
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 54a44a8..a585aa8 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -161,6 +161,13 @@
                 + "}";
     }
 
+    /** @hide */
+    @Override
+    public String toDebugString() {
+        return String.format("Step=%dms(amplitude=%.2f%s)", mDuration, mAmplitude,
+                Float.compare(mFrequencyHz, 0) == 0 ? "" : " @ " + mFrequencyHz + "Hz");
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index 4790d81..bde334a 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -32,6 +32,9 @@
 import android.os.VibrationAttributes;
 import android.os.Vibrator;
 import android.os.Vibrator.VibrationIntensity;
+import android.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
 
 /**
  * List of device-specific internal vibration configuration loaded from platform config.xml.
@@ -191,4 +194,18 @@
                 + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
                 + "}";
     }
+
+    /**
+     * Write current settings into given {@link PrintWriter}, skipping the default settings.
+     *
+     * @hide
+     */
+    public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
+        pw.println("VibrationConfig:");
+        pw.increaseIndent();
+        pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
+        pw.println("rampStepDurationMs = " + mRampStepDurationMs);
+        pw.println("rampDownDurationMs = " + mRampDownDurationMs);
+        pw.decreaseIndent();
+    }
 }
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 9a19ed4..3b286a7 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -116,6 +116,13 @@
     public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
 
     /**
+     * Returns a compact version of the {@link #toString()} result for debugging purposes.
+     *
+     * @hide
+     */
+    public abstract String toDebugString();
+
+    /**
      * Checks the given frequency argument is valid to represent a vibration effect frequency in
      * hertz, i.e. a finite non-negative value.
      *
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c1474eb..d3b7a5b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -71,6 +71,7 @@
 import android.window.ISurfaceSyncGroupCompletedListener;
 import android.window.ITaskFpsCallback;
 import android.window.ScreenCapture;
+import android.window.WindowContextInfo;
 
 /**
  * System private interface to the window manager.
@@ -858,10 +859,10 @@
      * @param displayId The display associated with the window context
      * @param options A bundle used to pass window-related options and choose the right DisplayArea
      *
-     * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
-     * attached to the DisplayArea successfully. {@code null}, otherwise.
+     * @return the {@link WindowContextInfo} of the DisplayArea if the WindowContext is attached to
+     * the DisplayArea successfully. {@code null}, otherwise.
      */
-    @nullable Configuration attachWindowContextToDisplayArea(in IApplicationThread appThread,
+    @nullable WindowContextInfo attachWindowContextToDisplayArea(in IApplicationThread appThread,
             IBinder clientToken, int type, int displayId, in @nullable Bundle options);
 
     /**
@@ -879,13 +880,15 @@
      * the WindowContext's token}
      * @param token the WindowToken to attach
      *
+     * @return the {@link WindowContextInfo} of the WindowToken if the WindowContext is attached to
+     * the WindowToken successfully. {@code null}, otherwise.
      * @throws IllegalArgumentException if the {@code clientToken} have not been attached to
      * the server or the WindowContext's type doesn't match WindowToken {@code token}'s type.
      *
      * @see #attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
      */
-    void attachWindowContextToWindowToken(in IApplicationThread appThread, IBinder clientToken,
-            IBinder token);
+    @nullable WindowContextInfo  attachWindowContextToWindowToken(in IApplicationThread appThread,
+            IBinder clientToken, IBinder token);
 
     /**
      * Attaches a {@code clientToken} to associate with DisplayContent.
@@ -899,11 +902,11 @@
      * the WindowContext's token}
      * @param displayId The display associated with the window context
      *
-     * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
-     * attached to the DisplayContent successfully. {@code null}, otherwise.
+     * @return the {@link WindowContextInfo} of the DisplayContent if the WindowContext is attached
+     * to the DisplayContent successfully. {@code null}, otherwise.
      * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
      */
-    @nullable Configuration attachWindowContextToDisplayContent(in IApplicationThread appThread,
+    @nullable WindowContextInfo attachWindowContextToDisplayContent(in IApplicationThread appThread,
             IBinder clientToken, int displayId);
 
     /**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 17afd55..4ecfc40 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -844,12 +844,12 @@
     public boolean onStateChanged(InsetsState state) {
         boolean stateChanged = false;
         if (!CAPTION_ON_SHELL) {
-            stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
-                    false /* excludeInvisibleIme */)
+            stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
+                    false /* excludesInvisibleIme */)
                     || captionInsetsUnchanged();
         } else {
-            stateChanged = !mState.equals(state, false /* excludingCaptionInsets */,
-                    false /* excludeInvisibleIme */);
+            stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+                    false /* excludesInvisibleIme */);
         }
         if (!stateChanged && mLastDispatchedState.equals(state)) {
             return false;
@@ -862,8 +862,8 @@
         applyLocalVisibilityOverride();
         updateCompatSysUiVisibility();
 
-        if (!mState.equals(lastState, false /* excludingCaptionInsets */,
-                true /* excludeInvisibleIme */)) {
+        if (!mState.equals(lastState, false /* excludesCaptionBar */,
+                true /* excludesInvisibleIme */)) {
             if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
             mHost.notifyInsetsChanged();
             if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c13b9ab..af24140 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -380,11 +380,17 @@
             @InternalInsetsSide @Nullable SparseIntArray idSideMap,
             @Nullable boolean[] typeVisibilityMap, Insets insets, int type) {
         int index = indexOf(type);
-        Insets existing = typeInsetsMap[index];
-        if (existing == null) {
-            typeInsetsMap[index] = insets;
-        } else {
-            typeInsetsMap[index] = Insets.max(existing, insets);
+
+        // Don't put Insets.NONE into typeInsetsMap. Otherwise, two WindowInsets can be considered
+        // as non-equal while they provide the same insets of each type from WindowInsets#getInsets
+        // if one WindowInsets has Insets.NONE for a type and the other has null for the same type.
+        if (!Insets.NONE.equals(insets)) {
+            Insets existing = typeInsetsMap[index];
+            if (existing == null) {
+                typeInsetsMap[index] = insets;
+            } else {
+                typeInsetsMap[index] = Insets.max(existing, insets);
+            }
         }
 
         if (typeVisibilityMap != null) {
@@ -696,15 +702,14 @@
      * An equals method can exclude the caption insets. This is useful because we assemble the
      * caption insets information on the client side, and when we communicate with server, it's
      * excluded.
-     * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
-     *                                           ignore the caption insets source value.
-     * @param excludeInvisibleImeFrames If {@link WindowInsets.Type#ime()} frames should be ignored
-     *                                  when IME is not visible.
+     * @param excludesCaptionBar If {@link Type#captionBar()}} should be ignored.
+     * @param excludesInvisibleIme If {@link WindowInsets.Type#ime()} should be ignored when IME is
+     *                             not visible.
      * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
      */
     @VisibleForTesting
-    public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
-            boolean excludeInvisibleImeFrames) {
+    public boolean equals(@Nullable Object o, boolean excludesCaptionBar,
+            boolean excludesInvisibleIme) {
         if (this == o) { return true; }
         if (o == null || getClass() != o.getClass()) { return false; }
 
@@ -721,29 +726,35 @@
 
         final SparseArray<InsetsSource> thisSources = mSources;
         final SparseArray<InsetsSource> thatSources = state.mSources;
-        if (!excludingCaptionInsets && !excludeInvisibleImeFrames) {
+        if (!excludesCaptionBar && !excludesInvisibleIme) {
             return thisSources.contentEquals(thatSources);
         } else {
             final int thisSize = thisSources.size();
             final int thatSize = thatSources.size();
             int thisIndex = 0;
             int thatIndex = 0;
-            while (thisIndex < thisSize && thatIndex < thatSize) {
+            while (thisIndex < thisSize || thatIndex < thatSize) {
+                InsetsSource thisSource = thisIndex < thisSize
+                        ? thisSources.valueAt(thisIndex)
+                        : null;
+
                 // Seek to the next non-excluding source of ours.
-                InsetsSource thisSource = thisSources.valueAt(thisIndex);
                 while (thisSource != null
-                        && (excludingCaptionInsets && thisSource.getType() == captionBar()
-                                || excludeInvisibleImeFrames && thisSource.getType() == ime()
+                        && (excludesCaptionBar && thisSource.getType() == captionBar()
+                                || excludesInvisibleIme && thisSource.getType() == ime()
                                         && !thisSource.isVisible())) {
                     thisIndex++;
                     thisSource = thisIndex < thisSize ? thisSources.valueAt(thisIndex) : null;
                 }
 
+                InsetsSource thatSource = thatIndex < thatSize
+                        ? thatSources.valueAt(thatIndex)
+                        : null;
+
                 // Seek to the next non-excluding source of theirs.
-                InsetsSource thatSource = thatSources.valueAt(thatIndex);
                 while (thatSource != null
-                        && (excludingCaptionInsets && thatSource.getType() == captionBar()
-                                || excludeInvisibleImeFrames && thatSource.getType() == ime()
+                        && (excludesCaptionBar && thatSource.getType() == captionBar()
+                                || excludesInvisibleIme && thatSource.getType() == ime()
                                         && !thatSource.isVisible())) {
                     thatIndex++;
                     thatSource = thatIndex < thatSize ? thatSources.valueAt(thatIndex) : null;
@@ -756,7 +767,7 @@
                 thisIndex++;
                 thatIndex++;
             }
-            return thisIndex >= thisSize && thatIndex >= thatSize;
+            return true;
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 01a99b9..1b1098d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2539,7 +2539,7 @@
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
             final float x = event.getXDispatchLocation(0);
-            final float y = event.getXDispatchLocation(0);
+            final float y = event.getYDispatchLocation(0);
 
             final ArrayList<View> preorderedList = buildOrderedChildList();
             final boolean customOrder = preorderedList == null
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 36eef53..c9ac245 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -137,12 +137,14 @@
      * @see WindowProviderService#attachToWindowToken(IBinder))
      * @see IWindowManager#attachWindowContextToWindowToken
      */
-    public void attachToWindowToken(IBinder windowToken) {
+    public void attachToWindowToken(@NonNull IBinder windowToken) {
         if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("The Window Context should have been attached"
                     + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
         }
-        getWindowTokenClientController().attachToWindowToken(mToken, windowToken);
+        if (!getWindowTokenClientController().attachToWindowToken(mToken, windowToken)) {
+            Log.e(TAG, "attachToWindowToken fail");
+        }
     }
 
     /** Detaches the window context from the node it's currently associated with. */
diff --git a/core/java/android/window/WindowContextInfo.aidl b/core/java/android/window/WindowContextInfo.aidl
new file mode 100644
index 0000000..360431c
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/** @hide */
+parcelable WindowContextInfo;
\ No newline at end of file
diff --git a/core/java/android/window/WindowContextInfo.java b/core/java/android/window/WindowContextInfo.java
new file mode 100644
index 0000000..3c21cd4
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Stores information about a particular window that a {@link WindowContext} is attached to.
+ * @hide
+ */
+public class WindowContextInfo implements Parcelable {
+
+    /**
+     * Configuration of the window.
+     */
+    @NonNull
+    private final Configuration mConfiguration;
+
+    /**
+     * The display id that the window is attached to.
+     */
+    private final int mDisplayId;
+
+    public WindowContextInfo(@NonNull Configuration configuration, int displayId) {
+        mConfiguration = requireNonNull(configuration);
+        mDisplayId = displayId;
+    }
+
+    @NonNull
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mConfiguration, flags);
+        dest.writeInt(mDisplayId);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextInfo(@NonNull Parcel in) {
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+        mDisplayId = in.readInt();
+    }
+
+    public static final @NonNull Creator<WindowContextInfo> CREATOR = new Creator<>() {
+        public WindowContextInfo createFromParcel(Parcel in) {
+            return new WindowContextInfo(in);
+        }
+
+        public WindowContextInfo[] newArray(int size) {
+            return new WindowContextInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextInfo other = (WindowContextInfo) o;
+        return Objects.equals(mConfiguration, other.mConfiguration)
+                && mDisplayId == other.mDisplayId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mConfiguration);
+        result = 31 * result + mDisplayId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextInfo{config=" + mConfiguration
+                + ", displayId=" + mDisplayId
+                + "}";
+    }
+}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 7458563..6a32529 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -99,6 +99,13 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
         // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
+        postOnConfigurationChanged(newConfig, newDisplayId);
+    }
+
+    /**
+     * Posts an {@link #onConfigurationChanged} to the main thread.
+     */
+    public void postOnConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId) {
         mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
                 newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
     }
@@ -162,7 +169,6 @@
                 windowContext.dispatchConfigurationChanged(newConfig);
             }
 
-
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 14b9df6..7a84123 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -22,10 +22,9 @@
 import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
-import android.app.servertransaction.WindowContextConfigurationChangeItem;
+import android.app.servertransaction.WindowContextInfoChangeItem;
 import android.app.servertransaction.WindowContextWindowRemovalItem;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -94,17 +93,17 @@
      */
     public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
             @WindowType int type, int displayId, @Nullable Bundle options) {
-        final Configuration configuration;
+        final WindowContextInfo info;
         try {
-            configuration = getWindowManagerService().attachWindowContextToDisplayArea(
+            info = getWindowManagerService().attachWindowContextToDisplayArea(
                     mAppThread, client, type, displayId, options);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        if (configuration == null) {
+        if (info == null) {
             return false;
         }
-        onWindowContextTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
         return true;
     }
 
@@ -121,16 +120,16 @@
         if (wms == null) {
             return false;
         }
-        final Configuration configuration;
+        final WindowContextInfo info;
         try {
-            configuration = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
+            info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        if (configuration == null) {
+        if (info == null) {
             return false;
         }
-        onWindowContextTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
         return true;
     }
 
@@ -139,19 +138,23 @@
      *
      * @param client The {@link WindowTokenClient} to attach.
      * @param windowToken the window token to associated with
+     * @return {@code true} if attaching successfully.
      */
-    public void attachToWindowToken(@NonNull WindowTokenClient client,
+    public boolean attachToWindowToken(@NonNull WindowTokenClient client,
             @NonNull IBinder windowToken) {
+        final WindowContextInfo info;
         try {
-            getWindowManagerService().attachWindowContextToWindowToken(
+            info = getWindowManagerService().attachWindowContextToWindowToken(
                     mAppThread, client, windowToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        // We don't report configuration change for now.
-        synchronized (mLock) {
-            mWindowTokenClientMap.put(client.asBinder(), client);
+        if (info == null) {
+            return false;
         }
+        // We currently report configuration for WindowToken after attached.
+        onWindowContextTokenAttached(client, info, true /* shouldReportConfigChange */);
+        return true;
     }
 
     /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
@@ -168,21 +171,30 @@
         }
     }
 
-    private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
-            @NonNull Configuration configuration) {
+    private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
+            @NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
         synchronized (mLock) {
             mWindowTokenClientMap.put(client.asBinder(), client);
         }
-        client.onConfigurationChanged(configuration, displayId,
-                false /* shouldReportConfigChange */);
+        if (shouldReportConfigChange) {
+            // Should trigger an #onConfigurationChanged callback to the WindowContext. Post the
+            // dispatch in the next loop to prevent the callback from being dispatched before
+            // #onCreate or WindowContext creation..
+            client.postOnConfigurationChanged(info.getConfiguration(), info.getDisplayId());
+        } else {
+            // Apply the config change directly in case users get stale values after WindowContext
+            // creation.
+            client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId(),
+                    false /* shouldReportConfigChange */);
+        }
     }
 
-    /** Called when receives {@link WindowContextConfigurationChangeItem}. */
-    public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId) {
+    /** Called when receives {@link WindowContextInfoChangeItem}. */
+    public void onWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info) {
         final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
         if (windowTokenClient != null) {
-            windowTokenClient.onConfigurationChanged(configuration, displayId);
+            windowTokenClient.onConfigurationChanged(info.getConfiguration(), info.getDisplayId());
         }
     }
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 325ebbe..8e619a8 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -609,4 +609,6 @@
     optional bool animation_in_progress = 1;
     optional int32 last_back_type = 2;
     optional bool show_wallpaper = 3;
+    optional string main_open_activity = 4;
+    optional bool animation_running = 5;
 }
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54dff08..64be87b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3938,8 +3938,14 @@
     <!-- Flag indicating device support for EAP SIM, AKA, AKA' -->
     <bool name="config_eap_sim_based_auth_supported">true</bool>
 
+    <!-- How long history of recent vibrations should be kept for the dumpsys. -->
+    <integer name="config_recentVibrationsDumpSizeLimit">20</integer>
+
     <!-- How long history of previous vibrations should be kept for the dumpsys. -->
-    <integer name="config_previousVibrationsDumpLimit">50</integer>
+    <integer name="config_previousVibrationsDumpSizeLimit">50</integer>
+
+    <!-- How close vibration request should be when they're aggregated for dumpsys, in ms. -->
+    <integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer>
 
     <!-- The default vibration strength, must be between 1 and 255 inclusive. -->
     <integer name="config_defaultVibrationAmplitude">255</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9da0f17..3aae3ca 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2038,7 +2038,9 @@
   <java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" />
   <java-symbol type="integer" name="config_notificationServiceArchiveSize" />
   <java-symbol type="dimen" name="config_rotaryEncoderAxisScrollTickInterval" />
-  <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
+  <java-symbol type="integer" name="config_recentVibrationsDumpSizeLimit" />
+  <java-symbol type="integer" name="config_previousVibrationsDumpSizeLimit" />
+  <java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
   <java-symbol type="integer" name="config_defaultVibrationAmplitude" />
   <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
   <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8da6d74..c904d96 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -64,6 +64,7 @@
 import android.util.MergedConfiguration;
 import android.view.Display;
 import android.view.View;
+import android.window.WindowContextInfo;
 import android.window.WindowTokenClientController;
 
 import androidx.test.filters.MediumTest;
@@ -753,13 +754,12 @@
         WindowTokenClientController.overrideForTesting(windowTokenClientController);
         final IBinder clientToken = mock(IBinder.class);
         final Configuration configuration = new Configuration();
+        final WindowContextInfo info = new WindowContextInfo(configuration, DEFAULT_DISPLAY);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
-                .handleWindowContextConfigurationChanged(
-                        clientToken, configuration, DEFAULT_DISPLAY));
+                .handleWindowContextInfoChanged(clientToken, info));
 
-        verify(windowTokenClientController).onWindowContextConfigurationChanged(
-                clientToken, configuration, DEFAULT_DISPLAY);
+        verify(windowTokenClientController).onWindowContextInfoChanged(clientToken, info);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
similarity index 79%
rename from core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
rename to core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
index 7811e1a..37a517e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
@@ -23,6 +23,7 @@
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
+import android.window.WindowContextInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -30,12 +31,12 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * Tests for {@link WindowContextConfigurationChangeItem}.
+ * Tests for {@link WindowContextInfoChangeItem}.
  *
  * Build/Install/Run:
- *  atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ *  atest FrameworksCoreTests:WindowContextInfoChangeItemTest
  */
-public class WindowContextConfigurationChangeItemTest {
+public class WindowContextInfoChangeItemTest {
 
     @Mock
     private ClientTransactionHandler mHandler;
@@ -55,11 +56,11 @@
 
     @Test
     public void testExecute() {
-        final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
                 .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
         item.execute(mHandler, mToken, mPendingActions);
 
-        verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
-                DEFAULT_DISPLAY);
+        verify(mHandler).handleWindowContextInfoChanged(mClientToken,
+                new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
     }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fde1a6d..b06cd39 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -43,6 +43,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -290,6 +291,18 @@
     }
 
     @Test
+    public void testCalculateInsets_emptyIme() {
+        WindowInsets insets1 = mState.calculateInsets(new Rect(), null, false, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        mState.getOrCreateSource(ID_IME, ime());
+        WindowInsets insets2 = mState.calculateInsets(new Rect(), null, false, false,
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        assertEquals(Insets.NONE, insets1.getInsets(ime()));
+        assertEquals(Insets.NONE, insets2.getInsets(ime()));
+        assertEquals(insets1, insets2);
+    }
+
+    @Test
     public void testStripForDispatch() {
         mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
                 .setFrame(new Rect(0, 0, 100, 100))
@@ -304,6 +317,73 @@
     }
 
     @Test
+    public void testEquals() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+        assertTrue(state1.equals(state2));
+
+        state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertFalse(state1.equals(state2));
+
+        state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertTrue(state1.equals(state2));
+
+        state2.addSource(new InsetsSource(ID_NAVIGATION_BAR, navigationBars()));
+        assertFalse(state1.equals(state2));
+    }
+
+    @Test
+    public void testEquals_excludesCaptionBar() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+
+        state1.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 5));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+        state2.addSource(new InsetsSource(ID_CAPTION_BAR, captionBar()).setFrame(0, 0, 0, 10));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+
+        state1.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        state2.addSource(new InsetsSource(ID_STATUS_BAR, statusBars()));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, true /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+    }
+
+    @Test
+    public void testEquals_excludesInvisibleIme() {
+        final InsetsState state1 = new InsetsState();
+        final InsetsState state2 = new InsetsState();
+
+        final InsetsSource imeSource1 = new InsetsSource(ID_IME, ime()).setVisible(true);
+        state1.addSource(imeSource1);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+        imeSource1.setVisible(false);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+
+        final InsetsSource imeSource2 = new InsetsSource(ID_IME, ime()).setFrame(0, 0, 0, 10);
+        state2.addSource(imeSource2);
+        assertFalse(state1.equals(
+                state2, false /* excludesCaptionBar */, false /* excludesInvisibleIme */));
+        assertTrue(state1.equals(
+                state2, false /* excludesCaptionBar */, true /* excludesInvisibleIme */));
+    }
+
+    @Test
     public void testEquals_differentRect() {
         mState.getOrCreateSource(ID_STATUS_BAR, statusBars())
                 .setFrame(new Rect(0, 0, 100, 100));
@@ -404,17 +484,6 @@
     }
 
     @Test
-    public void testEquals_excludeInvisibleIme() {
-        mState.getOrCreateSource(ID_IME, ime())
-                .setFrame(new Rect(0, 0, 100, 100))
-                .setVisible(false);
-        mState2.getOrCreateSource(ID_IME, ime())
-                .setFrame(new Rect(0, 0, 100, 200))
-                .setVisible(false);
-        assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
-    }
-
-    @Test
     public void testParcelUnparcel() {
         mState.getOrCreateSource(ID_IME, ime())
                 .setFrame(new Rect(0, 0, 100, 100))
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index b37c8fd..bce3f3e 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -49,11 +49,11 @@
     public void testDispatchMouseEventsUnderCursor() {
         final Context context = getInstrumentation().getContext();
         final TestView viewGroup = new TestView(context, 0 /* left */, 0 /* top */,
-                200 /* right */, 200 /* bottom */);
+                200 /* right */, 100 /* bottom */);
         final TestView viewA = spy(new TestView(context, 0 /* left */, 0 /* top */,
-                100 /* right */, 200 /* bottom */));
+                100 /* right */, 100 /* bottom */));
         final TestView viewB = spy(new TestView(context, 100 /* left */, 0 /* top */,
-                200 /* right */, 200 /* bottom */));
+                200 /* right */, 100 /* bottom */));
 
         viewGroup.addView(viewA);
         viewGroup.addView(viewB);
@@ -73,10 +73,10 @@
         MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[2];
         coords[0] = new MotionEvent.PointerCoords();
         coords[0].x = 80;
-        coords[0].y = 100;
+        coords[0].y = 50;
         coords[1] = new MotionEvent.PointerCoords();
         coords[1].x = 240;
-        coords[1].y = 100;
+        coords[1].y = 50;
 
         MotionEvent event;
         // Make sure the down event is active with a pointer which coordinate is different from the
@@ -91,6 +91,10 @@
         viewGroup.onResolvePointerIcon(event, 0 /* pointerIndex */);
         verify(viewB).onResolvePointerIcon(event, 0);
 
+        event.setAction(MotionEvent.ACTION_SCROLL);
+        viewGroup.dispatchGenericMotionEvent(event);
+        verify(viewB).dispatchGenericMotionEvent(event);
+
         event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
                 MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
                 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */,
@@ -102,8 +106,13 @@
         viewGroup.onResolvePointerIcon(event, 1 /* pointerIndex */);
         verify(viewB).onResolvePointerIcon(event, 1);
 
+        event.setAction(MotionEvent.ACTION_SCROLL);
+        viewGroup.dispatchGenericMotionEvent(event);
+        verify(viewB).dispatchGenericMotionEvent(event);
+
         verify(viewA, never()).dispatchTouchEvent(any());
         verify(viewA, never()).onResolvePointerIcon(any(), anyInt());
+        verify(viewA, never()).dispatchGenericMotionEvent(any());
     }
 
     /**
diff --git a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
index c4f7b3b..7bd6f05 100644
--- a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
@@ -64,6 +64,7 @@
     // Can't mock final class.
     private final Configuration mConfiguration = new Configuration();
 
+    private WindowContextInfo mWindowContextInfo;
     private WindowTokenClientController mController;
 
     @Before
@@ -72,6 +73,7 @@
         doReturn(mClientToken).when(mWindowTokenClient).asBinder();
         mController = spy(WindowTokenClientController.createInstanceForTesting());
         doReturn(mWindowManagerService).when(mController).getWindowManagerService();
+        mWindowContextInfo = new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY);
     }
 
     @Test
@@ -86,7 +88,7 @@
                 TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
                 any(), any(), anyInt(), anyInt(), any());
 
         assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
@@ -109,7 +111,7 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
                 any(), any(), anyInt(), anyInt(), any());
         mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
                 DEFAULT_DISPLAY, null /* options */);
@@ -129,8 +131,8 @@
                 DEFAULT_DISPLAY);
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
-                any(), any(), anyInt());
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToDisplayContent(any(), any(), anyInt());
 
         assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
         verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
@@ -150,8 +152,8 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
-                any(), any(), anyInt());
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToDisplayContent(any(), any(), anyInt());
         mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
         mController.detachIfNeeded(mWindowTokenClient);
 
@@ -160,11 +162,20 @@
 
     @Test
     public void testAttachToWindowToken() throws RemoteException {
-        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
 
+        assertFalse(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
         verify(mWindowManagerService).attachWindowContextToWindowToken(
                 ActivityThread.currentActivityThread().getApplicationThread(), mWindowTokenClient,
                 mWindowToken);
+        verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
+        verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
     }
 
     @Test
@@ -173,6 +184,15 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
+        doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContext(any());
+
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
         mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
         mController.detachIfNeeded(mWindowTokenClient);
 
@@ -180,28 +200,43 @@
     }
 
     @Test
-    public void testOnWindowContextConfigurationChanged() {
-        mController.onWindowContextConfigurationChanged(
-                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+    public void testOnWindowContextInfoChanged() throws RemoteException {
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        // No invoke if not attached.
+        mController.onWindowContextInfoChanged(mClientToken, mWindowContextInfo);
 
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
 
-        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        // Invoke postOnConfigurationChanged when attached
+        assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
 
-        mController.onWindowContextConfigurationChanged(
-                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+        verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
 
-        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+        // Invoke onConfigurationChanged when onWindowContextInfoChanged
+        mController.onWindowContextInfoChanged(
+                mClientToken, new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY + 1));
+
+        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY + 1);
     }
 
     @Test
-    public void testOnWindowContextWindowRemoved() {
+    public void testOnWindowContextWindowRemoved() throws RemoteException {
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        // No invoke if not attached.
         mController.onWindowContextWindowRemoved(mClientToken);
 
         verify(mWindowTokenClient, never()).onWindowTokenRemoved();
 
+        // No invoke if not onWindowTokenRemoved.
         mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
 
+        verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+        // Invoke onWindowTokenRemoved when onWindowContextWindowRemoved
         mController.onWindowContextWindowRemoved(mClientToken);
 
         verify(mWindowTokenClient).onWindowTokenRemoved();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index e69825f2..b0afa68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1053,6 +1053,20 @@
         mBubbleData.setExpanded(false /* expanded */);
     }
 
+    /**
+     * Update expanded state when a single bubble is dragged in Launcher.
+     * Will be called only when bubble bar is expanded.
+     * @param bubbleKey key of the bubble to collapse/expand
+     * @param isBeingDragged whether the bubble is being dragged
+     */
+    public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
+        if (mBubbleData.getSelectedBubble() != null
+                && mBubbleData.getSelectedBubble().getKey().equals(bubbleKey)) {
+            // Should collapse/expand only if equals to selected bubble.
+            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ !isBeingDragged);
+        }
+    }
+
     @VisibleForTesting
     public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
         boolean isSuppressedBubble = (mBubbleData.hasAnyBubbleWithKey(key)
@@ -1438,6 +1452,17 @@
         }
     }
 
+    /**
+     * Removes all the bubbles.
+     * <p>
+     * Must be called from the main thread.
+     */
+    @VisibleForTesting
+    @MainThread
+    public void removeAllBubbles(@Bubbles.DismissReason int reason) {
+        mBubbleData.dismissAll(reason);
+    }
+
     private void onEntryAdded(BubbleEntry entry) {
         if (canLaunchInTaskView(mContext, entry)) {
             updateBubble(entry);
@@ -2099,14 +2124,25 @@
         }
 
         @Override
-        public void removeBubble(String key, int reason) {
-            // TODO (b/271466616) allow removals from launcher
+        public void removeBubble(String key) {
+            mMainExecutor.execute(
+                    () -> mController.removeBubble(key, Bubbles.DISMISS_USER_GESTURE));
+        }
+
+        @Override
+        public void removeAllBubbles() {
+            mMainExecutor.execute(() -> mController.removeAllBubbles(Bubbles.DISMISS_USER_GESTURE));
         }
 
         @Override
         public void collapseBubbles() {
             mMainExecutor.execute(() -> mController.collapseStack());
         }
+
+        @Override
+        public void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
+            mMainExecutor.execute(() -> mController.onBubbleDrag(bubbleKey, isBeingDragged));
+        }
     }
 
     private class BubblesImpl implements Bubbles {
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 282db9e..f58b121 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
@@ -108,8 +108,10 @@
 public class BubbleStackView extends FrameLayout
         implements ViewTreeObserver.OnComputeInternalInsetsListener {
 
+    // LINT.IfChange
     public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
             SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
+    // LINT.ThenChange(com/android/launcher3/taskbar/bubbles/BubbleDismissController.java)
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 351319f..4dda068 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -31,8 +31,12 @@
 
     oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3;
 
-    oneway void removeBubble(in String key, in int reason) = 4;
+    oneway void removeBubble(in String key) = 4;
 
-    oneway void collapseBubbles() = 5;
+    oneway void removeAllBubbles() = 5;
+
+    oneway void collapseBubbles() = 6;
+
+    oneway void onBubbleDrag(in String key, in boolean isBeingDragged) = 7;
 
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index b3602b3..689323b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -215,6 +215,14 @@
         mExpandedViewAlphaAnimator.reverse();
     }
 
+    /**
+     * Cancel current animations
+     */
+    public void cancelAnimations() {
+        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+        mExpandedViewAlphaAnimator.cancel();
+    }
+
     private void updateExpandedView() {
         if (mExpandedBubble == null || mExpandedBubble.getBubbleBarExpandedView() == null) {
             Log.w(TAG, "Trying to update the expanded view without a bubble");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 8ead18b..bc04bfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -150,6 +150,12 @@
             mExpandedView = null;
         }
         if (mExpandedView == null) {
+            if (expandedView.getParent() != null) {
+                // Expanded view might be animating collapse and is still attached
+                // Cancel current animations and remove from parent
+                mAnimationHelper.cancelAnimations();
+                removeView(expandedView);
+            }
             mExpandedBubble = b;
             mExpandedView = expandedView;
             boolean isOverflowExpanded = b.getKey().equals(BubbleOverflow.KEY);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 4193ffa..ac8e4d4 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -64,6 +64,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -454,6 +455,7 @@
         assertThat(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
     }
 
+    @Ignore // TODO(b/291800179): Diagnose flakiness and re-enable.
     @Test
     public void testRouterRelease_managerGetRoutingSessions() throws Exception {
         CountDownLatch transferLatch = new CountDownLatch(1);
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index 6a5d2c0..fd394fc 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -12,6 +12,15 @@
            }
        ],
        "file_patterns": ["permission_manager.cpp"]
+    },
+    {
+       "name": "CtsOsTestCases",
+       "options": [
+           {
+              "include-filter": "android.os.cts.PerformanceHintManagerTest"
+           }
+       ],
+       "file_patterns": ["performance_hint.cpp"]
     }
   ]
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
index 8639f47..0d1475a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
@@ -16,6 +16,8 @@
 
 package com.android.packageinstaller;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
@@ -186,7 +188,9 @@
         int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
 
         if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
-            context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+            Intent intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+            intentToStart.addFlags(FLAG_ACTIVITY_NEW_TASK);
+            context.startActivity(intentToStart);
 
             return;
         }
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index 5d6c343..accaa67 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -25,10 +25,12 @@
     <color name="settingslib_color_blue100">#d2e3fc</color>
     <color name="settingslib_color_blue50">#e8f0fe</color>
     <color name="settingslib_color_green600">#1e8e3e</color>
+    <color name="settingslib_color_green500">#34A853</color>
     <color name="settingslib_color_green400">#5bb974</color>
     <color name="settingslib_color_green100">#ceead6</color>
     <color name="settingslib_color_green50">#e6f4ea</color>
     <color name="settingslib_color_red600">#d93025</color>
+    <color name="settingslib_color_red500">#B3261E</color>
     <color name="settingslib_color_red400">#ee675c</color>
     <color name="settingslib_color_red100">#fad2cf</color>
     <color name="settingslib_color_red50">#fce8e6</color>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 5e2c437..f166a18 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -42,9 +42,6 @@
                 ".grey600",
                 R.color.settingslib_color_grey400);
         map.put(
-                ".grey700",
-                R.color.settingslib_color_grey500);
-        map.put(
                 ".grey800",
                 R.color.settingslib_color_grey300);
         map.put(
@@ -62,6 +59,12 @@
         map.put(
                 ".green400",
                 R.color.settingslib_color_green600);
+        map.put(
+                ".green200",
+                R.color.settingslib_color_green500);
+        map.put(
+                ".red200",
+                R.color.settingslib_color_red500);
         DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
     }
 
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 5ed450a..c26cd12 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -25,8 +25,8 @@
 import android.os.Bundle;
 import android.provider.Browser;
 import android.provider.Settings;
-import android.widget.TextView;
 import android.view.View;
+import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
@@ -55,6 +55,10 @@
         ((TextView) findViewById(R.id.action_bar_title)).setText(
                 getResources().getString(R.string.accessibility_menu_settings_name)
         );
+        actionBar.setDisplayOptions(
+                ActionBar.DISPLAY_TITLE_MULTIPLE_LINES
+                        | ActionBar.DISPLAY_SHOW_TITLE
+                        | ActionBar.DISPLAY_HOME_AS_UP);
     }
 
     /**
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index d43276c..46f5971 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -190,6 +190,10 @@
         /** Flag denoting transit clock are enabled in wallpaper picker. */
         const val FLAG_NAME_PAGE_TRANSITIONS = "wallpaper_picker_page_transitions"
 
+        /** Flag denoting whether preview loading animation is enabled. */
+        const val FLAG_NAME_WALLPAPER_PICKER_PREVIEW_ANIMATION =
+            "wallpaper_picker_preview_animation"
+
         object Columns {
             /** String. Unique ID for the flag. */
             const val NAME = "name"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 4d3a742..2c224f62 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -423,6 +423,7 @@
      * Refresh clock. Called in response to TIME_TICK broadcasts.
      */
     void refresh() {
+        mLogBuffer.log(TAG, LogLevel.INFO, "refresh");
         if (mSmartspaceController != null) {
             mSmartspaceController.requestSmartspaceUpdate();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 61addab..8a0c9ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -1198,6 +1198,8 @@
                 });
                 mPopup.show();
             });
+
+            mUserSwitcherViewGroup.setAlpha(0f);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index a04d13b..8e92941 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,6 +20,7 @@
 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
@@ -51,6 +52,10 @@
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.shared.model.ScreenModel;
+import com.android.systemui.keyguard.shared.model.ScreenState;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -62,6 +67,9 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
+import kotlin.coroutines.CoroutineContext;
+import kotlin.coroutines.EmptyCoroutineContext;
+
 import java.io.PrintWriter;
 
 import javax.inject.Inject;
@@ -91,6 +99,7 @@
     private final FeatureFlags mFeatureFlags;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final Rect mClipBounds = new Rect();
+    private final KeyguardInteractor mKeyguardInteractor;
 
     private Boolean mStatusViewCentered = true;
 
@@ -122,6 +131,7 @@
             KeyguardLogger logger,
             FeatureFlags featureFlags,
             InteractionJankMonitor interactionJankMonitor,
+            KeyguardInteractor keyguardInteractor,
             DumpManager dumpManager) {
         super(keyguardStatusView);
         mKeyguardSliceViewController = keyguardSliceViewController;
@@ -134,12 +144,34 @@
         mInteractionJankMonitor = interactionJankMonitor;
         mFeatureFlags = featureFlags;
         mDumpManager = dumpManager;
+        mKeyguardInteractor = keyguardInteractor;
     }
 
     @Override
     public void onInit() {
         mKeyguardClockSwitchController.init();
         mDumpManager.registerDumpable(this);
+        if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+            startCoroutines(EmptyCoroutineContext.INSTANCE);
+        }
+    }
+
+    void startCoroutines(CoroutineContext context) {
+        collectFlow(mView, mKeyguardInteractor.getDozeTimeTick(),
+                (Long millis) -> {
+                        dozeTimeTick();
+                }, context);
+
+        collectFlow(mView, mKeyguardInteractor.getScreenModel(),
+                (ScreenModel model) -> {
+                    if (model.getState() == ScreenState.SCREEN_TURNING_ON) {
+                        dozeTimeTick();
+                    }
+                }, context);
+    }
+
+    public KeyguardStatusView getView() {
+        return mView;
     }
 
     @Override
@@ -308,6 +340,7 @@
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onTimeChanged() {
+            Slog.v(TAG, "onTimeChanged");
             refreshTime();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index a1b15f44..d82f458 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -122,9 +122,7 @@
         if (shouldAnimateIconViewForTransition(lastState, newState)) {
             iconView.playAnimation()
         }
-        if (isSideFps) {
-            LottieColorUtils.applyDynamicColors(context, iconView)
-        }
+        LottieColorUtils.applyDynamicColors(context, iconView)
     }
 
     override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index 34e934b..d9ec5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
@@ -120,6 +120,8 @@
                         viewModel.isShowing.collect { isShowing ->
                             view.visibility = if (isShowing) View.VISIBLE else View.INVISIBLE
                             if (isShowing) {
+                                // Reset security container because these views are not reinflated.
+                                securityContainerController.reset()
                                 securityContainerController.reinflateViewFlipper {
                                     // Reset Security Container entirely.
                                     securityContainerController.onBouncerVisibilityChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 1eba667..83bec66 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -124,8 +124,7 @@
     }
 
     private fun updateServices(newServices: List<ControlsServiceInfo>) {
-        if (featureFlags.isEnabled(Flags.USE_APP_PANELS) &&
-                activityTaskManagerProxy.supportsMultiWindow(context)) {
+        if (activityTaskManagerProxy.supportsMultiWindow(context)) {
             val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
             newServices.forEach {
                 it.resolvePanelActivity(allowAllApps) }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 3713811..a7e9efd8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -34,12 +34,9 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.controls.ControlsMetricsLogger
-import com.android.systemui.controls.settings.ControlsSettingsDialogManager
 import com.android.systemui.controls.settings.ControlsSettingsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -60,8 +57,6 @@
         private val controlsMetricsLogger: ControlsMetricsLogger,
         private val vibrator: VibratorHelper,
         private val controlsSettingsRepository: ControlsSettingsRepository,
-        private val controlsSettingsDialogManager: ControlsSettingsDialogManager,
-        private val featureFlags: FeatureFlags,
 ) : ControlActionCoordinator {
     private var dialog: Dialog? = null
     private var pendingAction: Action? = null
@@ -77,9 +72,6 @@
     }
 
     override fun closeDialogs() {
-        if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
-            controlsSettingsDialogManager.closeDialog()
-        }
         val isActivityFinishing =
             (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
         if (isActivityFinishing == true) {
@@ -169,7 +161,6 @@
     override fun runPendingAction(controlId: String) {
         if (isLocked) return
         if (pendingAction?.controlId == controlId) {
-            showSettingsDialogIfNeeded(pendingAction!!)
             pendingAction?.invoke()
             pendingAction = null
         }
@@ -208,7 +199,6 @@
                 true
             }, { pendingAction = null }, true /* afterKeyguardGone */)
         } else {
-            showSettingsDialogIfNeeded(action)
             action.invoke()
         }
     }
@@ -243,15 +233,6 @@
         }
     }
 
-    private fun showSettingsDialogIfNeeded(action: Action) {
-        if (action.authIsRequired) {
-            return
-        }
-        if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
-            controlsSettingsDialogManager.maybeShowDialog(activityContext) {}
-        }
-    }
-
     @VisibleForTesting
     fun createAction(
         controlId: String,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 557dcf4..8341964 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.controls.management.ControlsAnimations
 import com.android.systemui.controls.settings.ControlsSettingsDialogManager
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
 
@@ -66,9 +65,7 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         lastConfiguration.setTo(resources.configuration)
-        if (featureFlags.isEnabled(Flags.USE_APP_PANELS)) {
-            window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
-        }
+        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
 
         setContentView(R.layout.controls_fullscreen)
 
@@ -77,7 +74,7 @@
                 requireViewById(R.id.control_detail_root),
                 window,
                 intent,
-                !featureFlags.isEnabled(Flags.USE_APP_PANELS)
+                false
             )
         )
 
@@ -114,7 +111,7 @@
 
         parent = requireViewById(R.id.control_detail_root)
         parent.alpha = 0f
-        if (featureFlags.isEnabled(Flags.USE_APP_PANELS) && !keyguardStateController.isUnlocked) {
+        if (!keyguardStateController.isUnlocked) {
             controlsSettingsDialogManager.maybeShowDialog(this) {
                 uiController.show(parent, { finishOrReturnToDream() }, this)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 1b9be96..7e4f501 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -283,6 +283,15 @@
     // TODO(b/291767565): Tracking bug.
     @JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
 
+    /** Enables preview loading animation in the wallpaper picker. */
+    // TODO(b/274443705): Tracking Bug
+    @JvmField
+    val WALLPAPER_PICKER_PREVIEW_ANIMATION =
+            unreleasedFlag(
+                    244,
+                    "wallpaper_picker_preview_animation"
+            )
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -617,8 +626,6 @@
     @JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag")
 
     // 2000 - device controls
-    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels")
-
     @JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag(2001, "app_panels_all_apps_allowed")
 
     @JvmField
@@ -705,7 +712,7 @@
     // TODO(b/285059790) : Tracking Bug
     @JvmField
     val LOCKSCREEN_WALLPAPER_DREAM_ENABLED =
-        unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream")
+        unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream", teamfood = true)
 
     // TODO(b/283084712): Tracking Bug
     @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 82d0c8b..2686233 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -100,6 +100,7 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -173,6 +174,8 @@
 import dagger.Lazy;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Objects;
@@ -257,6 +260,22 @@
     private static final int SYSTEM_READY = 18;
     private static final int CANCEL_KEYGUARD_EXIT_ANIM = 19;
 
+    /** Enum for reasons behind updating wakeAndUnlock state. */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+            value = {
+                    WakeAndUnlockUpdateReason.HIDE,
+                    WakeAndUnlockUpdateReason.SHOW,
+                    WakeAndUnlockUpdateReason.FULFILL,
+                    WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK,
+            })
+    @interface WakeAndUnlockUpdateReason {
+        int HIDE = 0;
+        int SHOW = 1;
+        int FULFILL = 2;
+        int WAKE_AND_UNLOCK = 3;
+    }
+
     /**
      * The default amount of time we stay awake (used for all key input)
      */
@@ -808,7 +827,7 @@
             // dreaming. It's time to wake up.
             if (mUnlockingAndWakingFromDream) {
                 Log.d(TAG, "waking from dream after unlock");
-                mUnlockingAndWakingFromDream = false;
+                setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.FULFILL);
 
                 if (mKeyguardStateController.isShowing()) {
                     Log.d(TAG, "keyguard showing after keyguardGone, dismiss");
@@ -2693,7 +2712,7 @@
 
             mKeyguardExitAnimationRunner = null;
             mWakeAndUnlocking = false;
-            mUnlockingAndWakingFromDream = false;
+            setUnlockAndWakeFromDream(false, WakeAndUnlockUpdateReason.SHOW);
             setPendingLock(false);
 
             // Force if we we're showing in the middle of hiding, to ensure we end up in the correct
@@ -2799,6 +2818,51 @@
         tryKeyguardDone();
     };
 
+    private void setUnlockAndWakeFromDream(boolean updatedValue,
+            @WakeAndUnlockUpdateReason int reason) {
+        if (updatedValue == mUnlockingAndWakingFromDream) {
+            return;
+        }
+
+        final String reasonDescription;
+
+        switch(reason) {
+            case WakeAndUnlockUpdateReason.FULFILL:
+                reasonDescription = "fulfilling existing request";
+                break;
+            case WakeAndUnlockUpdateReason.HIDE:
+                reasonDescription = "hiding keyguard";
+                break;
+            case WakeAndUnlockUpdateReason.SHOW:
+                reasonDescription = "showing keyguard";
+                break;
+            case WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK:
+                reasonDescription = "waking to unlock";
+                break;
+            default:
+                throw new IllegalStateException("Unexpected value: " + reason);
+        }
+
+        final boolean unsetUnfulfilled = !updatedValue
+                && reason != WakeAndUnlockUpdateReason.FULFILL;
+
+        mUnlockingAndWakingFromDream = updatedValue;
+
+        final String description;
+
+        if (unsetUnfulfilled) {
+            description = "Interrupting request to wake and unlock";
+        } else if (mUnlockingAndWakingFromDream) {
+            description = "Initiating request to wake and unlock";
+        } else {
+            description = "Fulfilling request to wake and unlock";
+        }
+
+        Log.d(TAG, String.format(
+                "Updating waking and unlocking request to %b. description:[%s]. reason:[%s]",
+                mUnlockingAndWakingFromDream,  description, reasonDescription));
+    }
+
     /**
      * Handle message sent by {@link #hideLocked()}
      * @see #HIDE
@@ -2816,8 +2880,11 @@
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
 
-            mUnlockingAndWakingFromDream = mStatusBarStateController.isDreaming()
-                && !mStatusBarStateController.isDozing();
+            // If waking and unlocking, waking from dream has been set properly.
+            if (!mWakeAndUnlocking) {
+                setUnlockAndWakeFromDream(mStatusBarStateController.isDreaming()
+                        && mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE);
+            }
 
             if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) {
                 if (mUnlockingAndWakingFromDream) {
@@ -3319,9 +3386,14 @@
         }
     }
 
-    public void onWakeAndUnlocking() {
+    /**
+     * Informs the keyguard view mediator that the device is waking and unlocking.
+     * @param fromDream Whether waking and unlocking is happening over an interactive dream.
+     */
+    public void onWakeAndUnlocking(boolean fromDream) {
         Trace.beginSection("KeyguardViewMediator#onWakeAndUnlocking");
         mWakeAndUnlocking = true;
+        setUnlockAndWakeFromDream(fromDream, WakeAndUnlockUpdateReason.WAKE_AND_UNLOCK);
 
         mKeyguardViewControllerLazy.get().notifyKeyguardAuthenticated(/* primaryAuth */ false);
         userActivity();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 9ee9902..f1b3441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -47,16 +47,15 @@
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
@@ -177,7 +176,7 @@
     val keyguardRootViewVisibility: Flow<KeyguardRootViewVisibilityState>
 
     /** Receive an event for doze time tick */
-    val dozeTimeTick: Flow<Unit>
+    val dozeTimeTick: Flow<Long>
 
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
@@ -248,6 +247,7 @@
     private val dreamOverlayCallbackController: DreamOverlayCallbackController,
     @Main private val mainDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
+    private val systemClock: SystemClock,
 ) : KeyguardRepository {
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
@@ -398,11 +398,11 @@
         _isDozing.value = isDozing
     }
 
-    private val _dozeTimeTick = MutableSharedFlow<Unit>()
-    override val dozeTimeTick = _dozeTimeTick.asSharedFlow()
+    private val _dozeTimeTick = MutableStateFlow<Long>(0)
+    override val dozeTimeTick = _dozeTimeTick.asStateFlow()
 
     override fun dozeTimeTick() {
-        _dozeTimeTick.tryEmit(Unit)
+        _dozeTimeTick.value = systemClock.uptimeMillis()
     }
 
     private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 252982f..ac936b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -24,14 +24,11 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
@@ -46,24 +43,17 @@
     private val burnInHelperWrapper: BurnInHelperWrapper,
     @Application private val scope: CoroutineScope,
     private val configurationRepository: ConfigurationRepository,
-    private val systemClock: SystemClock,
+    private val keyguardInteractor: KeyguardInteractor,
 ) {
-    private val _dozeTimeTick = MutableStateFlow<Long>(0)
-    val dozeTimeTick: StateFlow<Long> = _dozeTimeTick.asStateFlow()
-
     val udfpsBurnInXOffset: StateFlow<Int> =
         burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
     val udfpsBurnInYOffset: StateFlow<Int> =
         burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
     val udfpsBurnInProgress: StateFlow<Float> =
-        dozeTimeTick
+        keyguardInteractor.dozeTimeTick
             .mapLatest { burnInHelperWrapper.burnInProgressOffset() }
             .stateIn(scope, SharingStarted.Lazily, burnInHelperWrapper.burnInProgressOffset())
 
-    fun dozeTimeTick() {
-        _dozeTimeTick.value = systemClock.uptimeMillis()
-    }
-
     /**
      * Use for max burn-in offsets that are NOT specified in pixels. This flow will recalculate the
      * max burn-in offset on any configuration changes. If the max burn-in offset is specified in
@@ -77,7 +67,9 @@
             .flatMapLatest {
                 val maxBurnInOffsetPixels =
                     context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
-                dozeTimeTick.mapLatest { calculateOffset(maxBurnInOffsetPixels, isXAxis) }
+                keyguardInteractor.dozeTimeTick.mapLatest {
+                    calculateOffset(maxBurnInOffsetPixels, isXAxis)
+                }
             }
             .stateIn(
                 scope,
@@ -102,7 +94,9 @@
             .flatMapLatest { scale ->
                 val maxBurnInOffsetPixels =
                     context.resources.getDimensionPixelSize(maxBurnInOffsetResourceId)
-                dozeTimeTick.mapLatest { calculateOffset(maxBurnInOffsetPixels, isXAxis, scale) }
+                keyguardInteractor.dozeTimeTick.mapLatest {
+                    calculateOffset(maxBurnInOffsetPixels, isXAxis, scale)
+                }
             }
             .stateIn(
                 scope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
new file mode 100644
index 0000000..dac6ef5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class FromDreamingLockscreenHostedTransitionInteractor
+@Inject
+constructor(
+    override val transitionRepository: KeyguardTransitionRepository,
+    override val transitionInteractor: KeyguardTransitionInteractor,
+    @Application private val scope: CoroutineScope,
+    private val keyguardInteractor: KeyguardInteractor,
+) :
+    TransitionInteractor(
+        fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+    ) {
+
+    override fun start() {
+        listenForDreamingLockscreenHostedToLockscreen()
+        listenForDreamingLockscreenHostedToGone()
+        listenForDreamingLockscreenHostedToDozing()
+        listenForDreamingLockscreenHostedToOccluded()
+        listenForDreamingLockscreenHostedToPrimaryBouncer()
+    }
+
+    private fun listenForDreamingLockscreenHostedToLockscreen() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                // Add a slight delay to prevent transitioning to lockscreen from happening too soon
+                // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
+                .onEach { delay(50) }
+                .sample(
+                    combine(
+                        keyguardInteractor.dozeTransitionModel,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect {
+                    (isActiveDreamLockscreenHosted, dozeTransitionModel, lastStartedTransition) ->
+                    if (
+                        !isActiveDreamLockscreenHosted &&
+                            DozeStateModel.isDozeOff(dozeTransitionModel.to) &&
+                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.LOCKSCREEN)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToOccluded() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardOccluded,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair,
+                    ),
+                    ::toTriple
+                )
+                .collect { (isActiveDreamLockscreenHosted, isOccluded, lastStartedTransition) ->
+                    if (
+                        isOccluded &&
+                            !isActiveDreamLockscreenHosted &&
+                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.OCCLUDED)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isBouncerShowing, lastStartedTransitionStep) ->
+                    if (
+                        isBouncerShowing &&
+                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToGone() {
+        scope.launch {
+            keyguardInteractor.biometricUnlockState
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+                    if (
+                        lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
+                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+                    ) {
+                        startTransitionTo(KeyguardState.GONE)
+                    }
+                }
+        }
+    }
+
+    private fun listenForDreamingLockscreenHostedToDozing() {
+        scope.launch {
+            combine(
+                    keyguardInteractor.dozeTransitionModel,
+                    transitionInteractor.startedKeyguardTransitionStep,
+                    ::Pair
+                )
+                .collect { (dozeTransitionModel, lastStartedTransitionStep) ->
+                    if (
+                        dozeTransitionModel.to == DozeStateModel.DOZE &&
+                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+                    ) {
+                        startTransitionTo(KeyguardState.DOZING)
+                    }
+                }
+        }
+    }
+
+    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
+        return ValueAnimator().apply {
+            interpolator = Interpolators.LINEAR
+            duration =
+                if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
+                else DEFAULT_DURATION.inWholeMilliseconds
+        }
+    }
+
+    companion object {
+        private val DEFAULT_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = 1167.milliseconds
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 98d7434..954ff6f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -94,11 +94,16 @@
 
     private fun listenForDreamingToGone() {
         scope.launch {
-            keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
-                if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
-                    startTransitionTo(KeyguardState.GONE)
+            keyguardInteractor.biometricUnlockState
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
+                    if (
+                        lastStartedTransitionStep.to == KeyguardState.DREAMING &&
+                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+                    ) {
+                        startTransitionTo(KeyguardState.GONE)
+                    }
                 }
-            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index f82633f..2b08b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -48,6 +48,7 @@
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
         listenForGoneToLockscreen()
+        listenForGoneToDreamingLockscreenHosted()
     }
 
     // Primarily for when the user chooses to lock down the device
@@ -63,12 +64,35 @@
         }
     }
 
+    private fun listenForGoneToDreamingLockscreenHosted() {
+        scope.launch {
+            keyguardInteractor.isActiveDreamLockscreenHosted
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
+                    if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
+                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                    }
+                }
+        }
+    }
+
     private fun listenForGoneToDreaming() {
         scope.launch {
             keyguardInteractor.isAbleToDream
-                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (isAbleToDream, lastStartedStep) ->
-                    if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
+                .sample(
+                    combine(
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isAbleToDream, lastStartedStep, isActiveDreamLockscreenHosted) ->
+                    if (
+                        isAbleToDream &&
+                            lastStartedStep.to == KeyguardState.GONE &&
+                            !isActiveDreamLockscreenHosted
+                    ) {
                         startTransitionTo(KeyguardState.DREAMING)
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index ed1bf3e..6b28b27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -70,17 +70,27 @@
                     combine(
                         transitionInteractor.startedKeyguardTransitionStep,
                         transitionInteractor.finishedKeyguardState,
-                        ::Pair
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::Triple
                     ),
-                    ::toTriple
+                    ::toQuad
                 )
-                .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
+                .collect {
+                    (
+                        isAbleToDream,
+                        lastStartedTransition,
+                        finishedKeyguardState,
+                        isActiveDreamLockscreenHosted) ->
                     val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
                     val isTransitionInterruptible =
                         lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
                             !invalidFromStates.contains(lastStartedTransition.from)
                     if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
-                        startTransitionTo(KeyguardState.DREAMING)
+                        if (isActiveDreamLockscreenHosted) {
+                            startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                        } else {
+                            startTransitionTo(KeyguardState.DREAMING)
+                        }
                     }
                 }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index e1754f5..9142d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
+import com.android.systemui.util.kotlin.Utils.Companion.toQuint
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -52,6 +54,7 @@
         listenForPrimaryBouncerToGone()
         listenForPrimaryBouncerToAodOrDozing()
         listenForPrimaryBouncerToLockscreenOrOccluded()
+        listenForPrimaryBouncerToDreamingLockscreenHosted()
     }
 
     private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -62,17 +65,24 @@
                         keyguardInteractor.wakefulnessModel,
                         transitionInteractor.startedKeyguardTransitionStep,
                         keyguardInteractor.isKeyguardOccluded,
-                        ::Triple
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        ::toQuad
                     ),
-                    ::toQuad
+                    ::toQuint
                 )
-                .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, occluded)
-                    ->
+                .collect {
+                    (
+                        isBouncerShowing,
+                        wakefulnessState,
+                        lastStartedTransitionStep,
+                        occluded,
+                        isActiveDreamLockscreenHosted) ->
                     if (
                         !isBouncerShowing &&
                             lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
                             (wakefulnessState.state == WakefulnessState.AWAKE ||
-                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE)
+                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) &&
+                            !isActiveDreamLockscreenHosted
                     ) {
                         startTransitionTo(
                             if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
@@ -111,6 +121,30 @@
         }
     }
 
+    private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(
+                    combine(
+                        keyguardInteractor.isActiveDreamLockscreenHosted,
+                        transitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect {
+                    (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
+                    if (
+                        !isBouncerShowing &&
+                            isActiveDreamLockscreenHosted &&
+                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+                    ) {
+                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+                    }
+                }
+        }
+    }
+
     private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index cc15916..53d3c07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -34,9 +34,9 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
+import com.android.systemui.keyguard.shared.model.ScreenModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -87,7 +87,7 @@
     /** Whether the system is in doze mode. */
     val isDozing: Flow<Boolean> = repository.isDozing
     /** Receive an event for doze time tick */
-    val dozeTimeTick: Flow<Unit> = repository.dozeTimeTick
+    val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
     /** Whether Always-on Display mode is available. */
     val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
     /** Doze transition information. */
@@ -100,7 +100,7 @@
     /** Whether the system is dreaming with an overlay active */
     val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
     /** Whether the system is dreaming and the active dream is hosted in lockscreen */
-    val isActiveDreamLockscreenHosted: Flow<Boolean> = repository.isActiveDreamLockscreenHosted
+    val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
     /** Event for when the camera gesture is detected */
     val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
         val callback =
@@ -122,6 +122,9 @@
     /** The device wake/sleep state */
     val wakefulnessModel: StateFlow<WakefulnessModel> = repository.wakefulness
 
+    /** The device screen state */
+    val screenModel: StateFlow<ScreenModel> = repository.screenModel
+
     /**
      * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
      * that doze mode is not running and DREAMING is ok to commence.
@@ -237,8 +240,16 @@
         repository.setQuickSettingsVisible(isVisible)
     }
 
-    fun setKeyguardRootVisibility(statusBarState: Int, goingToFullShade: Boolean, isOcclusionTransitionRunning: Boolean) {
-        repository.setKeyguardVisibility(statusBarState, goingToFullShade, isOcclusionTransitionRunning)
+    fun setKeyguardRootVisibility(
+        statusBarState: Int,
+        goingToFullShade: Boolean,
+        isOcclusionTransitionRunning: Boolean
+    ) {
+        repository.setKeyguardVisibility(
+            statusBarState,
+            goingToFullShade,
+            isOcclusionTransitionRunning
+        )
     }
 
     fun setClockPosition(x: Int, y: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index a486843..9c796f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -412,7 +412,11 @@
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_PAGE_TRANSITIONS,
                 value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS)
-            )
+            ),
+            KeyguardPickerFlag(
+                name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_PICKER_PREVIEW_ANIMATION,
+                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PREVIEW_ANIMATION)
+            ),
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index efc1bd0..ba7b987 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -45,6 +45,7 @@
                     is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
                     is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
+                    is FromDreamingLockscreenHostedTransitionInteractor -> Log.d(TAG, "Started $it")
                 }
             it.start()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 45bf20d..8c4c7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -89,12 +90,20 @@
     val dreamingToLockscreenTransition: Flow<TransitionStep> =
         repository.transition(DREAMING, LOCKSCREEN)
 
+    /** DREAMING_LOCKSCREEN_HOSTED->LOCKSCREEN transition information. */
+    val dreamingLockscreenHostedToLockscreenTransition: Flow<TransitionStep> =
+        repository.transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)
+
     /** GONE->AOD transition information. */
     val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
 
     /** GONE->DREAMING transition information. */
     val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
 
+    /** GONE->DREAMING_LOCKSCREEN_HOSTED transition information. */
+    val goneToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+        repository.transition(GONE, DREAMING_LOCKSCREEN_HOSTED)
+
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
@@ -102,6 +111,10 @@
     val lockscreenToDreamingTransition: Flow<TransitionStep> =
         repository.transition(LOCKSCREEN, DREAMING)
 
+    /** LOCKSCREEN->DREAMING_LOCKSCREEN_HOSTED transition information. */
+    val lockscreenToDreamingLockscreenHostedTransition: Flow<TransitionStep> =
+        repository.transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)
+
     /** LOCKSCREEN->OCCLUDED transition information. */
     val lockscreenToOccludedTransition: Flow<TransitionStep> =
         repository.transition(LOCKSCREEN, OCCLUDED)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 4244e55..6115d90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -92,6 +92,7 @@
                 KeyguardState.DOZING -> false
                 KeyguardState.AOD -> false
                 KeyguardState.DREAMING -> true
+                KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
                 KeyguardState.ALTERNATE_BOUNCER -> true
                 KeyguardState.PRIMARY_BOUNCER -> true
                 KeyguardState.LOCKSCREEN -> true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index d9690b7..56f5529 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -50,6 +50,12 @@
 
     @Binds
     @IntoSet
+    abstract fun fromDreamingLockscreenHosted(
+        impl: FromDreamingLockscreenHostedTransitionInteractor
+    ): TransitionInteractor
+
+    @Binds
+    @IntoSet
     abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 87b4321..1e20cdb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -35,6 +35,12 @@
      * parties to present their own UI over keyguard, like a screensaver.
      */
     DREAMING,
+    /*
+     * A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
+     * It is a special version of DREAMING state but not DOZING. The active dream will be windowless
+     * and hosted in the lockscreen.
+     */
+    DREAMING_LOCKSCREEN_HOSTED,
     /**
      * The device has entered a special low-power mode within SystemUI, also called the Always-on
      * Display (AOD). A minimal UI is presented to show critical information. If the device is in
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
new file mode 100644
index 0000000..113f01c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down GONE->DREAMING_LOCKSCREEN_HOSTED transition into discrete steps for corresponding
+ * views to consume.
+ */
+@SysUISingleton
+class GoneToDreamingLockscreenHostedTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_DREAMING_DURATION,
+            transitionFlow = interactor.goneToDreamingLockscreenHostedTransition,
+        )
+
+    /** Lockscreen views alpha - hide immediately */
+    val lockscreenAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 1.milliseconds,
+            onStep = { 0f },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index b307f1b..dd58607 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -112,6 +112,7 @@
             KeyguardState.OFF,
             KeyguardState.DOZING,
             KeyguardState.DREAMING,
+            KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
             KeyguardState.AOD,
             KeyguardState.PRIMARY_BOUNCER,
             KeyguardState.GONE,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 773f35e..2ea63c2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -140,6 +140,7 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -597,6 +598,9 @@
     private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
     private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
     private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
+    private final GoneToDreamingLockscreenHostedTransitionViewModel
+            mGoneToDreamingLockscreenHostedTransitionViewModel;
+
     private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
 
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -605,6 +609,7 @@
     private final CoroutineDispatcher mMainDispatcher;
     private boolean mIsAnyMultiShadeExpanded;
     private boolean mIsOcclusionTransitionRunning = false;
+    private boolean mIsGoneToDreamingLockscreenHostedTransitionRunning;
     private int mDreamingToLockscreenTransitionTranslationY;
     private int mOccludedToLockscreenTransitionTranslationY;
     private int mLockscreenToDreamingTransitionTranslationY;
@@ -652,6 +657,25 @@
                     step.getTransitionState() == TransitionState.RUNNING;
             };
 
+    private final Consumer<TransitionStep> mGoneToDreamingLockscreenHostedTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+                mIsGoneToDreamingLockscreenHostedTransitionRunning = mIsOcclusionTransitionRunning;
+            };
+
+    private final Consumer<TransitionStep> mLockscreenToDreamingLockscreenHostedTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+            };
+
+    private final Consumer<TransitionStep> mDreamingLockscreenHostedToLockscreenTransition =
+            (TransitionStep step) -> {
+                mIsOcclusionTransitionRunning =
+                        step.getTransitionState() == TransitionState.RUNNING;
+            };
+
     private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
             (TransitionStep step) -> {
                 mIsOcclusionTransitionRunning =
@@ -734,6 +758,8 @@
             OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
             LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
             GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
+            GoneToDreamingLockscreenHostedTransitionViewModel
+                    goneToDreamingLockscreenHostedTransitionViewModel,
             LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
             @Main CoroutineDispatcher mainDispatcher,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -761,6 +787,8 @@
         mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
         mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
         mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
+        mGoneToDreamingLockscreenHostedTransitionViewModel =
+                goneToDreamingLockscreenHostedTransitionViewModel;
         mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mKeyguardInteractor = keyguardInteractor;
@@ -1091,6 +1119,24 @@
                 mDreamingToLockscreenTransitionTranslationY),
                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
 
+        // Gone -> Dreaming hosted in lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getGoneToDreamingLockscreenHostedTransition(),
+                mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
+        collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
+                setTransitionAlpha(mNotificationStackScrollLayoutController),
+                mMainDispatcher);
+
+        // Lockscreen -> Dreaming hosted in lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getLockscreenToDreamingLockscreenHostedTransition(),
+                mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
+
+        // Dreaming hosted in lockscreen -> Lockscreen
+        collectFlow(mView, mKeyguardTransitionInteractor
+                        .getDreamingLockscreenHostedToLockscreenTransition(),
+                mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
+
         // Occluded->Lockscreen
         collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
                 mOccludedToLockscreenTransition, mMainDispatcher);
@@ -1496,13 +1542,18 @@
         mAnimateNextPositionUpdate = false;
     }
 
+    private boolean shouldAnimateKeyguardStatusViewAlignment() {
+        // Do not animate when transitioning from Gone->DreamingLockscreenHosted
+        return !mIsGoneToDreamingLockscreenHostedTransitionRunning;
+    }
+
     private void updateClockAppearance() {
         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
         boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
         mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
                 shouldAnimateClockChange);
-        updateKeyguardStatusViewAlignment(/* animate= */true);
+        updateKeyguardStatusViewAlignment(/* animate= */shouldAnimateKeyguardStatusViewAlignment());
         int userSwitcherHeight = mKeyguardQsUserSwitchController != null
                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
         if (mKeyguardUserSwitcherController != null) {
@@ -1625,6 +1676,10 @@
             // overlap.
             return true;
         }
+        if (isActiveDreamLockscreenHosted()) {
+            // Dreaming hosted in lockscreen, no "visible" notifications. Safe to center the clock.
+            return true;
+        }
         if (mNotificationListContainer.hasPulsingNotifications()) {
             // Pulsing notification appears on the right. Move clock left to avoid overlap.
             return false;
@@ -1653,6 +1708,11 @@
         return mDozing && mDozeParameters.getAlwaysOn();
     }
 
+
+    private boolean isActiveDreamLockscreenHosted() {
+        return mKeyguardInteractor.isActiveDreamLockscreenHosted().getValue();
+    }
+
     private boolean hasVisibleNotifications() {
         return mNotificationStackScrollLayoutController
                 .getVisibleNotificationCount() != 0
@@ -2820,7 +2880,9 @@
 
     @Override
     public void onScreenTurningOn() {
-        mKeyguardStatusViewController.dozeTimeTick();
+        if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+            mKeyguardStatusViewController.dozeTimeTick();
+        }
     }
 
     private void onMiddleClicked() {
@@ -3070,10 +3132,11 @@
         }
     }
 
-    @Override
     public void dozeTimeTick() {
         mLockIconViewController.dozeTimeTick();
-        mKeyguardStatusViewController.dozeTimeTick();
+        if (!mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+            mKeyguardStatusViewController.dozeTimeTick();
+        }
         if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 31d4ab9..ea3a8f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -91,6 +91,7 @@
         NotificationSectionHeadersModule.class,
         NotificationListViewModelModule.class,
         ActivatableNotificationViewModelModule.class,
+        NotificationMemoryModule.class,
 })
 public interface NotificationsModule {
     @Binds
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 035bdf1..f6ccaa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -471,7 +471,7 @@
         };
 
         final boolean wakingFromDream = mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM
-                && !mStatusBarStateController.isDozing();
+                && mPowerManager.isInteractive();
 
         if (mMode != MODE_NONE && !wakingFromDream) {
             wakeUp.run();
@@ -509,7 +509,7 @@
                     // later to awaken.
                 }
                 mNotificationShadeWindowController.setNotificationShadeFocusable(false);
-                mKeyguardViewMediator.onWakeAndUnlocking();
+                mKeyguardViewMediator.onWakeAndUnlocking(wakingFromDream);
                 Trace.endSection();
                 break;
             case MODE_ONLY_WAKE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ed9722e..801cdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -38,7 +38,6 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
 import com.android.systemui.shade.ShadeViewController;
@@ -94,7 +93,6 @@
     private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
     private final AuthController mAuthController;
     private final NotificationIconAreaController mNotificationIconAreaController;
-    private final BurnInInteractor mBurnInInteractor;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private ShadeViewController mNotificationPanel;
     private View mAmbientIndicationContainer;
@@ -118,8 +116,7 @@
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             AuthController authController,
             NotificationIconAreaController notificationIconAreaController,
-            DozeInteractor dozeInteractor,
-            BurnInInteractor burnInInteractor) {
+            DozeInteractor dozeInteractor) {
         super();
         mDozeLog = dozeLog;
         mPowerManager = powerManager;
@@ -138,7 +135,6 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mAuthController = authController;
         mNotificationIconAreaController = notificationIconAreaController;
-        mBurnInInteractor = burnInInteractor;
         mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener);
         mDozeInteractor = dozeInteractor;
     }
@@ -317,7 +313,6 @@
         if (mAmbientIndicationContainer instanceof DozeReceiver) {
             ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
         }
-        mBurnInInteractor.dozeTimeTick();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
index 06cc96e..d696979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java
@@ -46,6 +46,7 @@
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setClipChildren(false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index d39a53d..53f4837 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -44,6 +44,7 @@
 import android.media.session.MediaSession.Token;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -57,6 +58,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
 
+import androidx.annotation.NonNull;
 import androidx.lifecycle.Observer;
 
 import com.android.internal.annotations.GuardedBy;
@@ -65,6 +67,7 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.VolumeDialogController;
@@ -131,7 +134,7 @@
     private final Receiver mReceiver = new Receiver();
     private final RingerModeObservers mRingerModeObservers;
     private final MediaSessions mMediaSessions;
-    private final CaptioningManager mCaptioningManager;
+    private CaptioningManager mCaptioningManager;
     private final KeyguardManager mKeyguardManager;
     private final ActivityManager mActivityManager;
     private final UserTracker mUserTracker;
@@ -179,11 +182,11 @@
             AccessibilityManager accessibilityManager,
             PackageManager packageManager,
             WakefulnessLifecycle wakefulnessLifecycle,
-            CaptioningManager captioningManager,
             KeyguardManager keyguardManager,
             ActivityManager activityManager,
             UserTracker userTracker,
-            DumpManager dumpManager
+            DumpManager dumpManager,
+            @Main Handler mainHandler
     ) {
         mContext = context.getApplicationContext();
         mPackageManager = packageManager;
@@ -209,10 +212,12 @@
         mVibrator = vibrator;
         mHasVibrator = mVibrator.hasVibrator();
         mAudioService = iAudioService;
-        mCaptioningManager = captioningManager;
         mKeyguardManager = keyguardManager;
         mActivityManager = activityManager;
         mUserTracker = userTracker;
+        mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mainHandler));
+        createCaptioningManagerServiceByUserContext(mUserTracker.getUserContext());
+
         dumpManager.registerDumpable("VolumeDialogControllerImpl", this);
 
         boolean accessibilityVolumeStreamActive = accessibilityManager
@@ -316,6 +321,25 @@
         mWorker.sendEmptyMessage(W.GET_STATE);
     }
 
+    /**
+     * We met issues about the wrong state of System Caption in multi-user mode.
+     * It happened in the usage of CaptioningManager Service from SysUI process
+     * that is a global system process of User 0.
+     * Therefore, we have to add callback on UserTracker that allows us to get the Context of
+     * active User and then get the corresponding CaptioningManager Service for further usages.
+     */
+    private final UserTracker.Callback mUserChangedCallback =
+            new UserTracker.Callback() {
+                @Override
+                public void onUserChanged(int newUser, @NonNull Context userContext) {
+                    createCaptioningManagerServiceByUserContext(userContext);
+                }
+            };
+
+    private void createCaptioningManagerServiceByUserContext(@NonNull Context userContext) {
+        mCaptioningManager = userContext.getSystemService(CaptioningManager.class);
+    }
+
     public boolean areCaptionsEnabled() {
         return mCaptioningManager.isSystemAudioCaptioningEnabled();
     }
@@ -719,7 +743,7 @@
          * This method will never be called if the CSD (Computed Sound Dose) feature is
          * not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of
          * the feature.
-         * @param warning the type of warning to display, values are one of
+         * @param csdWarning the type of warning to display, values are one of
          *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X},
          *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X},
          *        {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE},
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
new file mode 100644
index 0000000..ba3dbf0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
+
+    @Mock protected KeyguardStatusView mKeyguardStatusView;
+    @Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
+    @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
+    @Mock protected KeyguardStateController mKeyguardStateController;
+    @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock protected ConfigurationController mConfigurationController;
+    @Mock protected DozeParameters mDozeParameters;
+    @Mock protected ScreenOffAnimationController mScreenOffAnimationController;
+    @Mock protected KeyguardLogger mKeyguardLogger;
+    @Mock protected KeyguardStatusViewController mControllerMock;
+    @Mock protected FeatureFlags mFeatureFlags;
+    @Mock protected InteractionJankMonitor mInteractionJankMonitor;
+    @Mock protected ViewTreeObserver mViewTreeObserver;
+    @Mock protected DumpManager mDumpManager;
+    protected FakeKeyguardRepository mFakeKeyguardRepository;
+
+    protected KeyguardStatusViewController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        KeyguardInteractorFactory.WithDependencies deps = KeyguardInteractorFactory.create();
+        mFakeKeyguardRepository = deps.getRepository();
+
+        mController = new KeyguardStatusViewController(
+                mKeyguardStatusView,
+                mKeyguardSliceViewController,
+                mKeyguardClockSwitchController,
+                mKeyguardStateController,
+                mKeyguardUpdateMonitor,
+                mConfigurationController,
+                mDozeParameters,
+                mScreenOffAnimationController,
+                mKeyguardLogger,
+                mFeatureFlags,
+                mInteractionJankMonitor,
+                deps.getKeyguardInteractor(),
+                mDumpManager) {
+                    @Override
+                    void setProperty(
+                            AnimatableProperty property,
+                            float value,
+                            boolean animate) {
+                        // Route into the mock version for verification
+                        mControllerMock.setProperty(property, value, animate);
+                    }
+                };
+
+        when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+    }
+
+    protected void givenViewAttached() {
+        ArgumentCaptor<View.OnAttachStateChangeListener> captor =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+        verify(mKeyguardStatusView, atLeast(1)).addOnAttachStateChangeListener(captor.capture());
+
+        for (View.OnAttachStateChangeListener listener : captor.getAllValues()) {
+            listener.onViewAttachedToWindow(mKeyguardStatusView);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 7114c22..20d9ef1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -23,80 +23,21 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ClockConfig;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 @SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
-public class KeyguardStatusViewControllerTest extends SysuiTestCase {
-
-    @Mock private KeyguardStatusView mKeyguardStatusView;
-    @Mock private KeyguardSliceViewController mKeyguardSliceViewController;
-    @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController;
-    @Mock private KeyguardStateController mKeyguardStateController;
-    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock private ConfigurationController mConfigurationController;
-    @Mock private DozeParameters mDozeParameters;
-    @Mock private ScreenOffAnimationController mScreenOffAnimationController;
-    @Mock private KeyguardLogger mKeyguardLogger;
-    @Mock private KeyguardStatusViewController mControllerMock;
-    @Mock private FeatureFlags mFeatureFlags;
-    @Mock private InteractionJankMonitor mInteractionJankMonitor;
-
-    @Mock private DumpManager mDumpManager;
-
-    @Captor
-    private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
-
-    private KeyguardStatusViewController mController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        mController = new KeyguardStatusViewController(
-                mKeyguardStatusView,
-                mKeyguardSliceViewController,
-                mKeyguardClockSwitchController,
-                mKeyguardStateController,
-                mKeyguardUpdateMonitor,
-                mConfigurationController,
-                mDozeParameters,
-                mScreenOffAnimationController,
-                mKeyguardLogger,
-                mFeatureFlags,
-                mInteractionJankMonitor,
-                mDumpManager) {
-                    @Override
-                    void setProperty(
-                            AnimatableProperty property,
-                            float value,
-                            boolean animate) {
-                        // Route into the mock version for verification
-                        mControllerMock.setProperty(property, value, animate);
-                    }
-                };
-    }
+public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest {
 
     @Test
     public void dozeTimeTick_updatesSlice() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
new file mode 100644
index 0000000..2b9797e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerWithCoroutinesTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class KeyguardStatusViewControllerWithCoroutinesTest : KeyguardStatusViewControllerBaseTest() {
+
+    @Test
+    fun dozeTimeTickUpdatesSlices() = runTest {
+        mController.startCoroutines(coroutineContext)
+        givenViewAttached()
+        runCurrent()
+        clearInvocations(mKeyguardSliceViewController)
+
+        mFakeKeyguardRepository.dozeTimeTick()
+        runCurrent()
+        verify(mKeyguardSliceViewController).refresh()
+
+        coroutineContext.cancelChildren()
+    }
+
+    @Test
+    fun onScreenTurningOnUpdatesSlices() = runTest {
+        mController.startCoroutines(coroutineContext)
+        givenViewAttached()
+        runCurrent()
+        clearInvocations(mKeyguardSliceViewController)
+
+        mFakeKeyguardRepository.setScreenModel(ScreenModel(ScreenState.SCREEN_ON))
+        runCurrent()
+        verify(mKeyguardSliceViewController, never()).refresh()
+
+        // Should only be called during a 'turning on' event
+        mFakeKeyguardRepository.setScreenModel(ScreenModel(ScreenState.SCREEN_TURNING_ON))
+        runCurrent()
+        verify(mKeyguardSliceViewController).refresh()
+
+        coroutineContext.cancelChildren()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 38372a3..692d794 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.controls.settings.ControlsSettingsDialogManager
 import com.android.systemui.controls.settings.FakeControlsSettingsRepository
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -36,7 +35,6 @@
 import org.mockito.Answers
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.doReturn
@@ -44,6 +42,7 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 import java.util.Optional
 
@@ -102,14 +101,11 @@
                 metricsLogger,
                 vibratorHelper,
                 controlsSettingsRepository,
-                controlsSettingsDialogManager,
-                featureFlags
         ))
         coordinator.activityContext = mContext
 
         `when`(cvh.cws.ci.controlId).thenReturn(ID)
         `when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
-        `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(false)
 
         action = spy(coordinator.Action(ID, {}, false, true))
         doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean())
@@ -155,13 +151,11 @@
         coordinator.toggle(cvh, "", true)
 
         verify(coordinator).bouncerOrRun(action)
-        verify(controlsSettingsDialogManager).maybeShowDialog(any(), any())
         verify(action).invoke()
     }
 
     @Test
     fun testToggleWhenLockedDoesNotTriggerDialog_featureFlagEnabled() {
-        `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(true)
         action = spy(coordinator.Action(ID, {}, false, false))
         doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean())
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index ee213f7..b1061ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -37,7 +37,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags.APP_PANELS_ALL_APPS_ALLOWED
-import com.android.systemui.flags.Flags.USE_APP_PANELS
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.ActivityTaskManagerProxy
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -124,8 +123,6 @@
                         arrayOf(componentName.packageName)
                 )
 
-        // Return true by default, we'll test the false path
-        `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(true)
         // Return false by default, we'll test the true path
         `when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(false)
 
@@ -445,34 +442,6 @@
     }
 
     @Test
-    fun testActivityDefaultEnabled_flagDisabled_nullPanel() {
-        `when`(featureFlags.isEnabled(USE_APP_PANELS)).thenReturn(false)
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName,
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        enabled = true,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
     fun testActivityDifferentPackage_nullPanel() {
         val serviceInfo = ServiceInfo(
                 componentName,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1f66e5b..28328c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -316,7 +316,7 @@
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(false);
         mViewMediator.onStartedWakingUp(OFF_BECAUSE_OF_USER, false);
         TestableLooper.get(this).processAllMessages();
 
@@ -378,6 +378,8 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
+        when(mPowerManager.isInteractive()).thenReturn(true);
+
         // Given device is dreaming
         when(mUpdateMonitor.isDreaming()).thenReturn(true);
 
@@ -717,14 +719,14 @@
 
     @Test
     public void testWakeAndUnlocking() {
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(false);
         verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
     }
 
     @Test
     public void testWakeAndUnlockingOverDream() {
         // Send signal to wake
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(true);
 
         // Ensure not woken up yet
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -753,7 +755,7 @@
     @Test
     public void testWakeAndUnlockingOverDream_signalAuthenticateIfStillShowing() {
         // Send signal to wake
-        mViewMediator.onWakeAndUnlocking();
+        mViewMediator.onWakeAndUnlocking(true);
 
         // Ensure not woken up yet
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
@@ -783,6 +785,35 @@
     }
 
     @Test
+    public void testWakeAndUnlockingOverNonInteractiveDream_noWakeByKeyguardViewMediator() {
+        // Send signal to wake
+        mViewMediator.onWakeAndUnlocking(false);
+
+        // Ensure not woken up yet
+        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+
+        // Verify keyguard told of authentication
+        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
+        mViewMediator.mViewMediatorCallback.keyguardDonePending(true,
+                mUpdateMonitor.getCurrentUser());
+        mViewMediator.mViewMediatorCallback.readyForKeyguardDone();
+        final ArgumentCaptor<Runnable> animationRunnableCaptor =
+                ArgumentCaptor.forClass(Runnable.class);
+        verify(mStatusBarKeyguardViewManager).startPreHideAnimation(
+                animationRunnableCaptor.capture());
+
+        when(mStatusBarStateController.isDreaming()).thenReturn(true);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        animationRunnableCaptor.getValue().run();
+
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        mViewMediator.mViewMediatorCallback.keyguardGone();
+
+        // Verify not woken up.
+        verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
+    }
+
+    @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testDoKeyguardWhileInteractive_resets() {
         mViewMediator.setShowingLocked(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index ba7d349..5e3376a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -85,13 +86,14 @@
     private val mainDispatcher = StandardTestDispatcher()
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
+    private lateinit var systemClock: FakeSystemClock
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
+        systemClock = FakeSystemClock()
         underTest =
             KeyguardRepositoryImpl(
                 statusBarStateController,
@@ -107,6 +109,7 @@
                 dreamOverlayCallbackController,
                 mainDispatcher,
                 testScope.backgroundScope,
+                systemClock,
             )
     }
 
@@ -167,11 +170,15 @@
     @Test
     fun dozeTimeTick() =
         testScope.runTest {
-            var dozeTimeTickValue = collectLastValue(underTest.dozeTimeTick)
-            underTest.dozeTimeTick()
-            runCurrent()
+            val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
+            assertThat(lastDozeTimeTick).isEqualTo(0L)
 
-            assertThat(dozeTimeTickValue()).isNull()
+            // WHEN dozeTimeTick updated
+            systemClock.setUptimeMillis(systemClock.uptimeMillis() + 5)
+            underTest.dozeTimeTick()
+
+            // THEN listeners were updated to the latest uptime millis
+            assertThat(systemClock.uptimeMillis()).isEqualTo(lastDozeTimeTick)
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index 069a486..6308269 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -23,10 +23,9 @@
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -48,7 +47,8 @@
     @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
 
     private lateinit var configurationRepository: FakeConfigurationRepository
-    private lateinit var systemClock: FakeSystemClock
+    private lateinit var keyguardInteractor: KeyguardInteractor
+    private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
     private lateinit var testScope: TestScope
     private lateinit var underTest: BurnInInteractor
 
@@ -56,8 +56,10 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         configurationRepository = FakeConfigurationRepository()
-        systemClock = FakeSystemClock()
-
+        KeyguardInteractorFactory.create().let {
+            keyguardInteractor = it.keyguardInteractor
+            fakeKeyguardRepository = it.repository
+        }
         whenever(burnInHelperWrapper.burnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
         setBurnInProgress(.65f)
 
@@ -68,26 +70,11 @@
                 burnInHelperWrapper,
                 testScope.backgroundScope,
                 configurationRepository,
-                systemClock,
+                keyguardInteractor,
             )
     }
 
     @Test
-    fun dozeTimeTick_updatesOnDozeTimeTick() =
-        testScope.runTest {
-            // Initial state set to 0
-            val lastDozeTimeTick by collectLastValue(underTest.dozeTimeTick)
-            assertEquals(0L, lastDozeTimeTick)
-
-            // WHEN dozeTimeTick updated
-            incrementUptimeMillis()
-            underTest.dozeTimeTick()
-
-            // THEN listeners were updated to the latest uptime millis
-            assertThat(systemClock.uptimeMillis()).isEqualTo(lastDozeTimeTick)
-        }
-
-    @Test
     fun udfpsBurnInOffset_updatesOnResolutionScaleChange() =
         testScope.runTest {
             val udfpsBurnInOffsetX by collectLastValue(underTest.udfpsBurnInXOffset)
@@ -111,25 +98,18 @@
             assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
 
             setBurnInProgress(.88f)
-            incrementUptimeMillis()
-            underTest.dozeTimeTick()
+            fakeKeyguardRepository.dozeTimeTick(10)
             assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
 
             setBurnInProgress(.92f)
-            incrementUptimeMillis()
-            underTest.dozeTimeTick()
+            fakeKeyguardRepository.dozeTimeTick(20)
             assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
 
             setBurnInProgress(.32f)
-            incrementUptimeMillis()
-            underTest.dozeTimeTick()
+            fakeKeyguardRepository.dozeTimeTick(30)
             assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
         }
 
-    private fun incrementUptimeMillis() {
-        systemClock.setUptimeMillis(systemClock.uptimeMillis() + 5)
-    }
-
     private fun setBurnInProgress(progress: Float) {
         burnInProgress = progress
         whenever(burnInHelperWrapper.burnInProgressOffset()).thenReturn(burnInProgress)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d01a46e..daf5ce6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -89,6 +89,8 @@
         FromAlternateBouncerTransitionInteractor
     private lateinit var fromPrimaryBouncerTransitionInteractor:
         FromPrimaryBouncerTransitionInteractor
+    private lateinit var fromDreamingLockscreenHostedTransitionInteractor:
+        FromDreamingLockscreenHostedTransitionInteractor
 
     @Before
     fun setUp() {
@@ -140,6 +142,15 @@
                 )
                 .apply { start() }
 
+        fromDreamingLockscreenHostedTransitionInteractor =
+            FromDreamingLockscreenHostedTransitionInteractor(
+                    scope = testScope,
+                    keyguardInteractor = createKeyguardInteractor(),
+                    transitionRepository = transitionRepository,
+                    transitionInteractor = transitionInteractor,
+                )
+                .apply { start() }
+
         fromAodTransitionInteractor =
             FromAodTransitionInteractor(
                     scope = testScope,
@@ -299,6 +310,38 @@
         }
 
     @Test
+    fun lockscreenToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to LOCKSCREEN
+            runTransition(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
+
+            // WHEN the device begins to dream and the dream is lockscreen hosted
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun lockscreenToDozing() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -353,6 +396,149 @@
         }
 
     @Test
+    fun dreamingLockscreenHostedToLockscreen() =
+        testScope.runTest {
+            // GIVEN a device dreaming with the lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the lockscreen hosted dream stops
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to Lockscreen should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToGone() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN biometrics succeeds with wake and unlock from dream mode
+            keyguardRepository.setBiometricUnlockState(
+                BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+            )
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to Gone should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.GONE)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToPrimaryBouncer() =
+        testScope.runTest {
+            // GIVEN a device dreaming with lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the primary bouncer is set to show
+            bouncerRepository.setPrimaryShow(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to PRIMARY_BOUNCER should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToDozing() =
+        testScope.runTest {
+            // GIVEN a device is dreaming with lockscreen hosted dream
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the device begins to sleep
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.INITIALIZED, to = DozeStateModel.DOZE)
+            )
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DOZING should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.DOZING)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun dreamingLockscreenHostedToOccluded() =
+        testScope.runTest {
+            // GIVEN device is dreaming with lockscreen hosted dream and not occluded
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            // GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
+            runTransition(KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+
+            // WHEN the keyguard is occluded and the lockscreen hosted dream stops
+            keyguardRepository.setIsActiveDreamLockscreenHosted(false)
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to OCCLUDED should occur
+            assertThat(info.ownerName).isEqualTo("FromDreamingLockscreenHostedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun dozingToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -533,6 +719,38 @@
         }
 
     @Test
+    fun goneToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN a device that is not dreaming or dozing
+            keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            runCurrent()
+
+            // GIVEN a prior transition has run to GONE
+            runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+            // WHEN the device begins to dream with the lockscreen hosted dream
+            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            advanceUntilIdle()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.GONE)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun alternateBouncerToPrimaryBouncer() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -726,6 +944,34 @@
         }
 
     @Test
+    fun primaryBouncerToDreamingLockscreenHosted() =
+        testScope.runTest {
+            // GIVEN device dreaming with the lockscreen hosted dream and not dozing
+            keyguardRepository.setIsActiveDreamLockscreenHosted(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
+
+            // GIVEN a prior transition has run to PRIMARY_BOUNCER
+            bouncerRepository.setPrimaryShow(true)
+            runTransition(KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
+
+            // WHEN the primary bouncer stops showing and lockscreen hosted dream still active
+            bouncerRepository.setPrimaryShow(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition back to DREAMING_LOCKSCREEN_HOSTED should occur
+            assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.to).isEqualTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun occludedToGone() =
         testScope.runTest {
             // GIVEN a device on lockscreen
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index b019a21..6efec99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -38,7 +38,6 @@
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -68,6 +67,7 @@
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var burnInInteractor: BurnInInteractor
     private lateinit var shadeRepository: FakeShadeRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
 
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
     @Mock private lateinit var dialogManager: SystemUIDialogManager
@@ -79,35 +79,32 @@
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
         configRepository = FakeConfigurationRepository()
-        keyguardRepository = FakeKeyguardRepository()
-        bouncerRepository = FakeKeyguardBouncerRepository()
-        shadeRepository = FakeShadeRepository()
-        fakeCommandQueue = FakeCommandQueue()
         featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
                 set(Flags.FACE_AUTH_REFACTOR, false)
             }
+        KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
+            keyguardInteractor = it.keyguardInteractor
+            keyguardRepository = it.repository
+        }
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        shadeRepository = FakeShadeRepository()
+        fakeCommandQueue = FakeCommandQueue()
         burnInInteractor =
             BurnInInteractor(
                 context,
                 burnInHelper,
                 testScope.backgroundScope,
                 configRepository,
-                FakeSystemClock(),
+                keyguardInteractor
             )
 
         underTest =
             UdfpsKeyguardInteractor(
                 configRepository,
                 burnInInteractor,
-                KeyguardInteractor(
-                    keyguardRepository,
-                    fakeCommandQueue,
-                    featureFlags,
-                    bouncerRepository,
-                    configRepository,
-                ),
+                keyguardInteractor,
                 shadeRepository,
                 dialogManager,
             )
@@ -215,7 +212,7 @@
 
     private fun setAwake() {
         keyguardRepository.setDozeAmount(0f)
-        burnInInteractor.dozeTimeTick()
+        keyguardRepository.dozeTimeTick()
 
         bouncerRepository.setAlternateVisible(false)
         keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index b985b3c..bd17de3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -20,21 +20,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.doze.util.BurnInHelperWrapper
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -58,9 +56,9 @@
     private lateinit var configRepository: FakeConfigurationRepository
     private lateinit var bouncerRepository: KeyguardBouncerRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
-    private lateinit var fakeCommandQueue: FakeCommandQueue
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var shadeRepository: FakeShadeRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
 
     @Mock private lateinit var dialogManager: SystemUIDialogManager
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
@@ -70,17 +68,21 @@
         MockitoAnnotations.initMocks(this)
         overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
         testScope = TestScope()
-        configRepository = FakeConfigurationRepository()
-        keyguardRepository = FakeKeyguardRepository()
-        bouncerRepository = FakeKeyguardBouncerRepository()
-        fakeCommandQueue = FakeCommandQueue()
         shadeRepository = FakeShadeRepository()
         featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
                 set(Flags.FACE_AUTH_REFACTOR, false)
             }
-
+        KeyguardInteractorFactory.create(
+                featureFlags = featureFlags,
+            )
+            .also {
+                keyguardInteractor = it.keyguardInteractor
+                keyguardRepository = it.repository
+                configRepository = it.configurationRepository
+                bouncerRepository = it.bouncerRepository
+            }
         val udfpsKeyguardInteractor =
             UdfpsKeyguardInteractor(
                 configRepository,
@@ -89,15 +91,9 @@
                     burnInHelper,
                     testScope.backgroundScope,
                     configRepository,
-                    FakeSystemClock(),
+                    keyguardInteractor,
                 ),
-                KeyguardInteractor(
-                    keyguardRepository,
-                    fakeCommandQueue,
-                    featureFlags,
-                    bouncerRepository,
-                    configRepository,
-                ),
+                keyguardInteractor,
                 shadeRepository,
                 dialogManager,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 0fbcec2..80ab418 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -30,12 +30,11 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -90,14 +89,10 @@
                 testScope.backgroundScope,
             )
         val keyguardInteractor =
-            KeyguardInteractor(
-                keyguardRepository,
-                fakeCommandQueue,
-                featureFlags,
-                bouncerRepository,
-                configRepository,
-            )
-
+            KeyguardInteractorFactory.create(
+                    featureFlags = featureFlags,
+                )
+                .keyguardInteractor
         underTest =
             FingerprintViewModel(
                 context,
@@ -109,7 +104,7 @@
                         burnInHelper,
                         testScope.backgroundScope,
                         configRepository,
-                        FakeSystemClock(),
+                        keyguardInteractor,
                     ),
                     keyguardInteractor,
                     shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index 41ae931..0456824 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -39,7 +40,6 @@
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.time.FakeSystemClock
 import com.android.wm.shell.animation.Interpolators
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
@@ -72,6 +72,7 @@
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
     private lateinit var configRepository: FakeConfigurationRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
     private lateinit var shadeRepository: FakeShadeRepository
     private lateinit var featureFlags: FakeFeatureFlags
@@ -81,23 +82,21 @@
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
         transitionRepository = FakeKeyguardTransitionRepository()
-        configRepository = FakeConfigurationRepository()
-        keyguardRepository = FakeKeyguardRepository()
-        bouncerRepository = FakeKeyguardBouncerRepository()
         shadeRepository = FakeShadeRepository()
         featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
                 set(Flags.FACE_AUTH_REFACTOR, false)
             }
-        val keyguardInteractor =
-            KeyguardInteractor(
-                keyguardRepository,
-                commandQueue = mock(),
-                featureFlags,
-                bouncerRepository,
-                configRepository,
+        KeyguardInteractorFactory.create(
+                featureFlags = featureFlags,
             )
+            .also {
+                keyguardInteractor = it.keyguardInteractor
+                keyguardRepository = it.repository
+                configRepository = it.configurationRepository
+                bouncerRepository = it.bouncerRepository
+            }
 
         underTest =
             UdfpsLockscreenViewModel(
@@ -115,7 +114,7 @@
                         burnInHelperWrapper = mock(),
                         testScope.backgroundScope,
                         configRepository,
-                        FakeSystemClock(),
+                        keyguardInteractor,
                     ),
                     keyguardInteractor,
                     shadeRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 47ca49d0..9bcc8aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -99,6 +99,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
@@ -297,7 +298,8 @@
     @Mock protected LockscreenToOccludedTransitionViewModel
             mLockscreenToOccludedTransitionViewModel;
     @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
-
+    @Mock protected GoneToDreamingLockscreenHostedTransitionViewModel
+            mGoneToDreamingLockscreenHostedTransitionViewModel;
     @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel;
     @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -371,6 +373,7 @@
                 mKeyguardLogger,
                 mFeatureFlags,
                 mInteractionJankMonitor,
+                mKeyguardInteractor,
                 mDumpManager));
 
         when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
@@ -477,6 +480,20 @@
         when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt()))
                 .thenReturn(emptyFlow());
 
+        // Gone->Dreaming lockscreen hosted
+        when(mKeyguardTransitionInteractor.getGoneToDreamingLockscreenHostedTransition())
+                .thenReturn(emptyFlow());
+        when(mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha())
+                .thenReturn(emptyFlow());
+
+        // Dreaming lockscreen hosted->Lockscreen
+        when(mKeyguardTransitionInteractor.getDreamingLockscreenHostedToLockscreenTransition())
+                .thenReturn(emptyFlow());
+
+        // Lockscreen->Dreaming lockscreen hosted
+        when(mKeyguardTransitionInteractor.getLockscreenToDreamingLockscreenHostedTransition())
+                .thenReturn(emptyFlow());
+
         // Lockscreen->Occluded
         when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition())
                 .thenReturn(emptyFlow());
@@ -612,6 +629,7 @@
                 mOccludedToLockscreenTransitionViewModel,
                 mLockscreenToDreamingTransitionViewModel,
                 mGoneToDreamingTransitionViewModel,
+                mGoneToDreamingLockscreenHostedTransitionViewModel,
                 mLockscreenToOccludedTransitionViewModel,
                 mMainDispatcher,
                 mKeyguardTransitionInteractor,
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 91aa138..481f7f7 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
@@ -199,7 +199,7 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
 
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
         assertThat(mBiometricUnlockController.getMode())
                 .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
     }
@@ -217,7 +217,7 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
 
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(false);
         assertThat(mBiometricUnlockController.getMode())
                 .isEqualTo(MODE_WAKE_AND_UNLOCK);
     }
@@ -671,8 +671,9 @@
         when(mWakefulnessLifecycle.getLastWakeReason())
                 .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
         givenDreamingLocked();
+        when(mPowerManager.isInteractive()).thenReturn(true);
         mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, true);
-        verify(mKeyguardViewMediator).onWakeAndUnlocking();
+        verify(mKeyguardViewMediator).onWakeAndUnlocking(true);
         // Ensure that the power hasn't been told to wake up yet.
         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 57037e0..ff6f40d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -43,7 +43,6 @@
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.domain.interactor.BurnInInteractor;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
 import com.android.systemui.shade.ShadeViewController;
@@ -96,7 +95,6 @@
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private AuthController mAuthController;
     @Mock private DozeHost.Callback mCallback;
-    @Mock private BurnInInteractor mBurnInInteractor;
 
     @Mock private DozeInteractor mDozeInteractor;
     @Before
@@ -108,8 +106,7 @@
                 () -> mAssistManager, mDozeScrimController,
                 mKeyguardUpdateMonitor, mPulseExpansionHandler,
                 mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
-                mAuthController, mNotificationIconAreaController, mDozeInteractor,
-                mBurnInInteractor);
+                mAuthController, mNotificationIconAreaController, mDozeInteractor);
 
         mDozeServiceHost.initialize(
                 mCentralSurfaces,
@@ -234,11 +231,11 @@
         verifyZeroInteractions(mDozeInteractor);
     }
     @Test
-    public void dozeTimeTickSentTBurnInInteractor() {
+    public void dozeTimeTickSentToDozeInteractor() {
         // WHEN dozeTimeTick
         mDozeServiceHost.dozeTimeTick();
 
-        // THEN burnInInteractor's dozeTimeTick is updated
-        verify(mBurnInInteractor).dozeTimeTick();
+        // THEN dozeInteractor's dozeTimeTick is updated
+        verify(mDozeInteractor).dozeTimeTick();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 0663004..462fd0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -40,7 +40,6 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.CaptioningManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -64,6 +63,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.Executor;
+
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
 @TestableLooper.RunWithLooper
@@ -96,8 +97,6 @@
     @Mock
     private WakefulnessLifecycle mWakefullnessLifcycle;
     @Mock
-    private CaptioningManager mCaptioningManager;
-    @Mock
     private KeyguardManager mKeyguardManager;
     @Mock
     private ActivityManager mActivityManager;
@@ -105,6 +104,8 @@
     private UserTracker mUserTracker;
     @Mock
     private DumpManager mDumpManager;
+    @Mock
+    private Handler mHandler;
 
 
     @Before
@@ -117,6 +118,7 @@
         when(mRingerModeLiveData.getValue()).thenReturn(-1);
         when(mRingerModeInternalLiveData.getValue()).thenReturn(-1);
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
+        when(mUserTracker.getUserContext()).thenReturn(mContext);
         // Enable group volume adjustments
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions,
@@ -127,8 +129,8 @@
         mVolumeController = new TestableVolumeDialogControllerImpl(mContext,
                 mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager,
                 mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager,
-                mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mKeyguardManager,
-                mActivityManager, mUserTracker, mDumpManager, mCallback);
+                mPackageManager, mWakefullnessLifcycle, mKeyguardManager,
+                mActivityManager, mUserTracker, mDumpManager, mHandler, mCallback);
         mVolumeController.setEnableDialogs(true, true);
     }
 
@@ -219,6 +221,11 @@
         verify(mRingerModeInternalLiveData).observeForever(any());
     }
 
+    @Test
+    public void testAddCallbackWithUserTracker() {
+        verify(mUserTracker).addCallback(any(UserTracker.Callback.class), any(Executor.class));
+    }
+
     static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
         private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver;
 
@@ -234,16 +241,16 @@
                 AccessibilityManager accessibilityManager,
                 PackageManager packageManager,
                 WakefulnessLifecycle wakefulnessLifecycle,
-                CaptioningManager captioningManager,
                 KeyguardManager keyguardManager,
                 ActivityManager activityManager,
                 UserTracker userTracker,
                 DumpManager dumpManager,
+                Handler mainHandler,
                 C callback) {
             super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager,
                     notificationManager, optionalVibrator, iAudioService, accessibilityManager,
-                    packageManager, wakefulnessLifecycle, captioningManager, keyguardManager,
-                    activityManager, userTracker, dumpManager);
+                    packageManager, wakefulnessLifecycle, keyguardManager,
+                    activityManager, userTracker, dumpManager, mainHandler);
             mCallbacks = callback;
 
             ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index e6894d7..15ce055 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -60,7 +59,7 @@
     private val _isDozing = MutableStateFlow(false)
     override val isDozing: StateFlow<Boolean> = _isDozing
 
-    private val _dozeTimeTick = MutableSharedFlow<Unit>()
+    private val _dozeTimeTick = MutableStateFlow<Long>(0L)
     override val dozeTimeTick = _dozeTimeTick
 
     private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
@@ -174,7 +173,11 @@
     }
 
     override fun dozeTimeTick() {
-        _dozeTimeTick.tryEmit(Unit)
+        _dozeTimeTick.value = _dozeTimeTick.value + 1
+    }
+
+    fun dozeTimeTick(millis: Long) {
+        _dozeTimeTick.value = millis
     }
 
     override fun setLastDozeTapToWakePosition(position: Point) {
@@ -237,6 +240,10 @@
         _isBypassEnabled = isEnabled
     }
 
+    fun setScreenModel(screenModel: ScreenModel) {
+        _screenModel.value = screenModel
+    }
+
     override fun isUdfpsSupported(): Boolean {
         return _isUdfpsSupported.value
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index d33d224..805f6e3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -110,6 +110,7 @@
     private boolean mAlwaysOnMagnificationEnabled = false;
     private final DisplayManagerInternal mDisplayManagerInternal;
 
+    private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag;
     @NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier;
 
     /**
@@ -689,6 +690,13 @@
             }
         }
 
+        void onThumbnailFeatureFlagChanged() {
+            synchronized (mLock) {
+                destroyThumbnail();
+                createThumbnailIfSupported();
+            }
+        }
+
         /**
          * Updates the current magnification spec.
          *
@@ -849,19 +857,43 @@
         addInfoChangedCallback(magnificationInfoChangedCallback);
         mScaleProvider = scaleProvider;
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+        mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag();
+        mMagnificationThumbnailFeatureFlag.addOnChangedListener(
+                backgroundExecutor, this::onMagnificationThumbnailFeatureFlagChanged);
         if (thumbnailSupplier != null) {
             mThumbnailSupplier = thumbnailSupplier;
         } else {
             mThumbnailSupplier = () -> {
-                return new MagnificationThumbnail(
-                        ctx.getContext(),
-                        ctx.getContext().getSystemService(WindowManager.class),
-                        new Handler(ctx.getContext().getMainLooper())
-                );
+                if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) {
+                    return new MagnificationThumbnail(
+                            ctx.getContext(),
+                            ctx.getContext().getSystemService(WindowManager.class),
+                            new Handler(ctx.getContext().getMainLooper())
+                    );
+                }
+                return null;
             };
         }
     }
 
+    private void onMagnificationThumbnailFeatureFlagChanged() {
+        synchronized (mLock) {
+            for (int i = 0; i < mDisplays.size(); i++) {
+                onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i));
+            }
+        }
+    }
+
+    private void onMagnificationThumbnailFeatureFlagChanged(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return;
+            }
+            display.onThumbnailFeatureFlagChanged();
+        }
+    }
+
     /**
      * Start tracking the magnification region for services that control magnification and the
      * magnification gesture handler.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
new file mode 100644
index 0000000..519f31b
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.magnification;
+
+import android.provider.DeviceConfig;
+
+/**
+ * Encapsulates the feature flags for magnification thumbnail. {@see DeviceConfig}
+ *
+ * @hide
+ */
+public class MagnificationThumbnailFeatureFlag extends MagnificationFeatureFlagBase {
+
+    private static final String NAMESPACE = DeviceConfig.NAMESPACE_ACCESSIBILITY;
+    private static final String FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL =
+            "enable_magnifier_thumbnail";
+
+    @Override
+    String getNamespace() {
+        return NAMESPACE;
+    }
+
+    @Override
+    String getFeatureName() {
+        return FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL;
+    }
+
+    @Override
+    boolean getDefaultValue() {
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index c6d6122..80d14a2 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -346,6 +346,9 @@
                         if (mHandler.hasMessages(CANCEL_GAME_LOADING_MODE)) {
                             mHandler.removeMessages(CANCEL_GAME_LOADING_MODE);
                         }
+                        Slog.v(TAG, String.format(
+                                "Game loading power mode %s (game state change isLoading=%b)",
+                                        isLoading ? "ON" : "OFF", isLoading));
                         mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
                         if (isLoading) {
                             int loadingBoostDuration = getLoadingBoostDuration(packageName, userId);
@@ -369,6 +372,7 @@
                     break;
                 }
                 case CANCEL_GAME_LOADING_MODE: {
+                    Slog.v(TAG, "Game loading power mode OFF (loading boost ended)");
                     mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
                     break;
                 }
@@ -1279,6 +1283,7 @@
                 // instruction.
                 mHandler.removeMessages(CANCEL_GAME_LOADING_MODE);
             } else {
+                Slog.v(TAG, "Game loading power mode ON (loading boost on game start)");
                 mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, true);
             }
 
@@ -1555,6 +1560,10 @@
                 }
             }
         }, new IntentFilter(Intent.ACTION_SHUTDOWN));
+        Slog.v(TAG, "Game loading power mode OFF (game manager service start/restart)");
+        mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
+        Slog.v(TAG, "Game power mode OFF (game manager service start/restart)");
+        mPowerManagerInternal.setPowerMode(Mode.GAME, false);
     }
 
     private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 5819ff0..5c1897d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -185,8 +185,7 @@
      * True if {@link #mWakeLock} is open for acquisition. It is set to false after the client is
      * unregistered.
      */
-    @GuardedBy("mWakeLock")
-    private boolean mIsWakelockUsable = true;
+    private AtomicBoolean mIsWakelockUsable = new AtomicBoolean(true);
 
     /*
      * Internal interface used to invoke client callbacks.
@@ -529,7 +528,7 @@
     @VisibleForTesting
     boolean isWakelockUsable() {
         synchronized (mWakeLock) {
-            return mIsWakelockUsable;
+            return mIsWakelockUsable.get();
         }
     }
 
@@ -1103,10 +1102,8 @@
     private void acquireWakeLock() {
         Binder.withCleanCallingIdentity(
                 () -> {
-                    synchronized (mWakeLock) {
-                        if (mIsWakelockUsable) {
-                            mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
-                        }
+                    if (mIsWakelockUsable.get()) {
+                        mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
                     }
                 });
     }
@@ -1119,13 +1116,11 @@
     private void releaseWakeLock() {
         Binder.withCleanCallingIdentity(
                 () -> {
-                    synchronized (mWakeLock) {
-                        if (mWakeLock.isHeld()) {
-                            try {
-                                mWakeLock.release();
-                            } catch (RuntimeException e) {
-                                Log.e(TAG, "Releasing the wakelock fails - ", e);
-                            }
+                    if (mWakeLock.isHeld()) {
+                        try {
+                            mWakeLock.release();
+                        } catch (RuntimeException e) {
+                            Log.e(TAG, "Releasing the wakelock fails - ", e);
                         }
                     }
                 });
@@ -1139,18 +1134,16 @@
     private void releaseWakeLockOnExit() {
         Binder.withCleanCallingIdentity(
                 () -> {
-                    synchronized (mWakeLock) {
-                        mIsWakelockUsable = false;
-                        while (mWakeLock.isHeld()) {
-                            try {
-                                mWakeLock.release();
-                            } catch (RuntimeException e) {
-                                Log.e(
-                                        TAG,
-                                        "Releasing the wakelock for all acquisitions fails - ",
-                                        e);
-                                break;
-                            }
+                    mIsWakelockUsable.set(false);
+                    while (mWakeLock.isHeld()) {
+                        try {
+                            mWakeLock.release();
+                        } catch (RuntimeException e) {
+                            Log.e(
+                                    TAG,
+                                    "Releasing the wakelock for all acquisitions fails - ",
+                                    e);
+                            break;
                         }
                     }
                 });
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index ec0d985..77a60289 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -123,7 +123,7 @@
      * @param userId The uid of the user whose profile has been unlocked.
      * @param credentialType The type of credential as defined in {@code LockPatternUtils}
      * @param credential The credential, encoded as a byte array
-     * @param credentialUpdated signals weather credentials were updated.
+     * @param credentialUpdated indicates credentials change.
      * @param platformKeyManager platform key manager
      * @param testOnlyInsecureCertificateHelper utility class used for end-to-end tests
      */
@@ -143,7 +143,7 @@
         mRecoverableKeyStoreDb = recoverableKeyStoreDb;
         mUserId = userId;
         mCredentialType = credentialType;
-        mCredential = credential;
+        mCredential = credential != null ? Arrays.copyOf(credential, credential.length) : null;
         mCredentialUpdated = credentialUpdated;
         mPlatformKeyManager = platformKeyManager;
         mRecoverySnapshotStorage = snapshotStorage;
@@ -160,6 +160,10 @@
             }
         } catch (Exception e) {
             Log.e(TAG, "Unexpected exception thrown during KeySyncTask", e);
+        } finally {
+            if (mCredential != null) {
+                Arrays.fill(mCredential, (byte) 0); // no longer needed.
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
index b5c0417..1ed829e4 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolverEngine.java
@@ -32,6 +32,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.UserProperties;
 import android.os.Process;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.util.Slog;
@@ -421,9 +422,19 @@
         } else {
             candidates.addAll(resolveInfoFromCrossProfileDomainInfo(crossProfileCandidates));
         }
+        // When we have only a single result belonging to a different user space, we check
+        // if filtering is configured on cross-profile intent resolution for the originating user.
+        // Accordingly, we modify the intent to reflect the content-owner as the originating user,
+        // preventing the resolving user to be assumed the same, when activity is started.
+
+        // In case more than one result is present, the resolver sheet is opened which takes care of
+        // cross user access.
+        if (candidates.size() == 1 && !UserHandle.of(userId).equals(candidates.get(0).userHandle)
+                && isNoFilteringPropertyConfiguredForUser(userId)) {
+            intent.prepareToLeaveUser(userId);
+        }
         return new QueryIntentActivitiesResult(sortResult, addInstant, candidates);
     }
-
     /**
      * It filters and combines results from current and cross profile based on domain priority.
      * @param computer {@link Computer} instance
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 80e07f4..10cd51a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1886,7 +1886,14 @@
         }
 
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
-            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+            if (success) {
+                // There is a timing issue here, if the callback opens the session again in
+                // notifySessionFinished() immediately, the session may not be removed from
+                // the mSession. But to avoid adding unknown latency, only notifying failures
+                // are moved to the last of posted runnable, notifying success cases are
+                // still kept here.
+                mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
+            }
 
             mInstallHandler.post(new Runnable() {
                 @Override
@@ -1915,6 +1922,10 @@
 
                         mSettingsWriteRequest.runNow();
                     }
+                    if (!success) {
+                        mCallbacks.notifySessionFinished(
+                                session.sessionId, session.userId, success);
+                    }
                 }
             });
         }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 710e0b7..dd434fbe 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -37,6 +37,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -1927,11 +1928,32 @@
         }
         if (shortcut.getIcon() != null) {
             ShortcutInfo.validateIcon(shortcut.getIcon());
+            validateIconURI(shortcut);
         }
 
         shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED);
     }
 
+    // Validates the calling process has permission to access shortcut icon's image uri
+    private void validateIconURI(@NonNull final ShortcutInfo si) {
+        final int callingUid = injectBinderCallingUid();
+        final Icon icon = si.getIcon();
+        if (icon == null) {
+            // There's no icon in this shortcut, nothing to validate here.
+            return;
+        }
+        int iconType = icon.getType();
+        if (iconType != Icon.TYPE_URI && iconType != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+            // The icon is not URI-based, nothing to validate.
+            return;
+        }
+        final Uri uri = icon.getUri();
+        mUriGrantsManagerInternal.checkGrantUriPermission(callingUid, si.getPackage(),
+                ContentProvider.getUriWithoutUserId(uri),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(callingUid)));
+    }
+
     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
         fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b01a89e..7897195 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -72,6 +72,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
@@ -93,7 +94,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -233,11 +233,11 @@
         }
 
         if (checkPermissionDelegate == null) {
-            return mPermissionManagerServiceImpl.checkPermission(
-                    packageName, permissionName, userId);
+            return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName,
+                    deviceId, userId);
         }
-        return checkPermissionDelegate.checkPermission(packageName, permissionName, userId,
-                mPermissionManagerServiceImpl::checkPermission);
+        return checkPermissionDelegate.checkPermission(packageName, permissionName,
+                deviceId, userId, mPermissionManagerServiceImpl::checkPermission);
     }
 
     @Override
@@ -254,10 +254,10 @@
         }
 
         if (checkPermissionDelegate == null)  {
-            return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName);
+            return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
         }
         return checkPermissionDelegate.checkUidPermission(uid, permissionName,
-                mPermissionManagerServiceImpl::checkUidPermission);
+                deviceId, mPermissionManagerServiceImpl::checkUidPermission);
     }
 
     @Override
@@ -511,14 +511,14 @@
     public int getPermissionFlags(String packageName, String permissionName, int deviceId,
             int userId) {
         return mPermissionManagerServiceImpl
-                .getPermissionFlags(packageName, permissionName, userId);
+                .getPermissionFlags(packageName, permissionName, deviceId, userId);
     }
 
     @Override
     public void updatePermissionFlags(String packageName, String permissionName, int flagMask,
             int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
         mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask,
-                flagValues, checkAdjustPolicyFlagPermission, userId);
+                flagValues, checkAdjustPolicyFlagPermission, deviceId, userId);
     }
 
     @Override
@@ -560,14 +560,15 @@
     @Override
     public void grantRuntimePermission(String packageName, String permissionName, int deviceId,
             int userId) {
-        mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId);
+        mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName,
+                deviceId, userId);
     }
 
     @Override
     public void revokeRuntimePermission(String packageName, String permissionName, int deviceId,
             int userId, String reason) {
         mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName,
-                userId, reason);
+                deviceId, userId, reason);
     }
 
     @Override
@@ -580,14 +581,14 @@
     public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int deviceId, int userId) {
         return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
-                permissionName, userId);
+                permissionName, deviceId, userId);
     }
 
     @Override
     public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
             int deviceId, int userId) {
-        return mPermissionManagerServiceImpl
-                .isPermissionRevokedByPolicy(packageName, permissionName, userId);
+        return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
+                permissionName, deviceId, userId);
     }
 
     @Override
@@ -868,6 +869,7 @@
          *
          * @param packageName the name of the package to be checked
          * @param permissionName the name of the permission to be checked
+         * @param deviceId The device ID
          * @param userId the user ID
          * @param superImpl the original implementation that can be delegated to
          * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
@@ -876,20 +878,21 @@
          * @see android.content.pm.PackageManager#checkPermission(String, String)
          */
         int checkPermission(@NonNull String packageName, @NonNull String permissionName,
-                @UserIdInt int userId,
-                @NonNull TriFunction<String, String, Integer, Integer> superImpl);
+                int deviceId, @UserIdInt int userId,
+                @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl);
 
         /**
          * Check whether the given UID has been granted the specified permission.
          *
          * @param uid the UID to be checked
          * @param permissionName the name of the permission to be checked
+         * @param deviceId The device ID
          * @param superImpl the original implementation that can be delegated to
          * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
          * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
          */
-        int checkUidPermission(int uid, @NonNull String permissionName,
-                BiFunction<Integer, String, Integer> superImpl);
+        int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+                TriFunction<Integer, String, Integer, Integer> superImpl);
 
         /**
          * @return list of delegated permissions
@@ -918,31 +921,32 @@
 
         @Override
         public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
-                int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
+                int deviceId, int userId,
+                @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) {
             if (mDelegatedPackageName.equals(packageName)
                     && isDelegatedPermission(permissionName)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply("com.android.shell", permissionName, userId);
+                    return superImpl.apply("com.android.shell", permissionName, deviceId, userId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(packageName, permissionName, userId);
+            return superImpl.apply(packageName, permissionName, deviceId, userId);
         }
 
         @Override
-        public int checkUidPermission(int uid, @NonNull String permissionName,
-                @NonNull BiFunction<Integer, String, Integer> superImpl) {
+        public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
+                @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
             if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(Process.SHELL_UID, permissionName);
+                    return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(uid, permissionName);
+            return superImpl.apply(uid, permissionName, deviceId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 4353c57..6764e08 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -681,7 +681,7 @@
     }
 
     @Override
-    public int getPermissionFlags(String packageName, String permName, int userId) {
+    public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
         final int callingUid = Binder.getCallingUid();
         return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
     }
@@ -724,7 +724,7 @@
 
     @Override
     public void updatePermissionFlags(String packageName, String permName, int flagMask,
-            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
         final int callingUid = Binder.getCallingUid();
         boolean overridePolicy = false;
 
@@ -908,8 +908,12 @@
         }
     }
 
+    private int checkPermission(String pkgName, String permName, int userId) {
+        return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId);
+    }
+
     @Override
-    public int checkPermission(String pkgName, String permName, int userId) {
+    public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
         if (!mUserManagerInt.exists(userId)) {
             return PackageManager.PERMISSION_DENIED;
         }
@@ -975,8 +979,12 @@
         return true;
     }
 
+    private int checkUidPermission(int uid, String permName) {
+        return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
+    }
+
     @Override
-    public int checkUidPermission(int uid, String permName) {
+    public int checkUidPermission(int uid, String permName, int deviceId) {
         final int userId = UserHandle.getUserId(uid);
         if (!mUserManagerInt.exists(userId)) {
             return PackageManager.PERMISSION_DENIED;
@@ -1295,7 +1303,8 @@
     }
 
     @Override
-    public void grantRuntimePermission(String packageName, String permName, final int userId) {
+    public void grantRuntimePermission(String packageName, String permName, int deviceId,
+            int userId) {
         final int callingUid = Binder.getCallingUid();
         final boolean overridePolicy =
                 checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1468,11 +1477,11 @@
     }
 
     @Override
-    public void revokeRuntimePermission(String packageName, String permName, int userId,
-            String reason) {
+    public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+            int userId, String reason) {
         final int callingUid = Binder.getCallingUid();
         final boolean overridePolicy =
-                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
+                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId)
                         == PackageManager.PERMISSION_GRANTED;
 
         revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
@@ -1859,7 +1868,7 @@
 
     @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
-            @UserIdInt int userId) {
+            int deviceId, @UserIdInt int userId) {
         final int callingUid = Binder.getCallingUid();
         if (UserHandle.getCallingUserId() != userId) {
             mContext.enforceCallingPermission(
@@ -1922,7 +1931,8 @@
     }
 
     @Override
-    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+            int userId) {
         if (UserHandle.getCallingUserId() != userId) {
             mContext.enforceCallingPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -2059,8 +2069,8 @@
                     continue;
                 }
                 boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(),
-                        permInfo.name, userId) & (FLAG_PERMISSION_SYSTEM_FIXED
-                        | FLAG_PERMISSION_POLICY_FIXED)) != 0;
+                        permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & (
+                        FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0;
                 if (isSystemOrPolicyFixed) {
                     continue;
                 }
@@ -2226,7 +2236,8 @@
                 for (final int userId : userIds) {
                     final int permissionState = checkPermission(packageName, permName,
                             userId);
-                    final int flags = getPermissionFlags(packageName, permName, userId);
+                    final int flags = getPermissionFlags(packageName, permName,
+                            Context.DEVICE_ID_DEFAULT, userId);
                     final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
                             | FLAG_PERMISSION_POLICY_FIXED
                             | FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -5122,8 +5133,7 @@
 
     @NonNull
     @Override
-    public Set<String> getGrantedPermissions(@NonNull String packageName,
-            @UserIdInt int userId) {
+    public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
         Objects.requireNonNull(packageName, "packageName");
         Preconditions.checkArgumentNonNegative(userId, "userId");
         return getGrantedPermissionsInternal(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 128f847..2d824aa 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,7 +25,6 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.permission.IOnPermissionsChangeListener;
-import android.permission.PermissionManager;
 import android.permission.PermissionManagerInternal;
 
 import com.android.server.pm.pkg.AndroidPackage;
@@ -137,14 +136,16 @@
     void removePermission(String permName);
 
     /**
-     * Gets the state flags associated with a permission.
+     * Gets the permission state flags associated with a permission.
      *
      * @param packageName the package name for which to get the flags
      * @param permName the permission for which to get the flags
+     * @param deviceId The device for which to get the flags
      * @param userId the user for which to get permission flags
      * @return the permission flags
      */
-    int getPermissionFlags(String packageName, String permName, int userId);
+    int getPermissionFlags(String packageName, String permName, int deviceId,
+            @UserIdInt int userId);
 
     /**
      * Updates the flags associated with a permission by replacing the flags in the specified mask
@@ -154,10 +155,11 @@
      * @param permName The permission for which to update the flags
      * @param flagMask The flags which to replace
      * @param flagValues The flags with which to replace
+     * @param deviceId The device for which to update the permission flags
      * @param userId The user for which to update the permission flags
      */
-    void updatePermissionFlags(String packageName, String permName, int flagMask,
-            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
+    void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
+            boolean checkAdjustPolicyFlagPermission, int deviceId, @UserIdInt int userId);
 
     /**
      * Update the permission flags for all packages and runtime permissions of a user in order
@@ -291,11 +293,13 @@
      *
      * @param packageName the package to which to grant the permission
      * @param permName the permission name to grant
+     * @param deviceId the device for which to grant the permission
      * @param userId the user for which to grant the permission
      *
-     * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
+     * @see #revokeRuntimePermission(String, String, int, int, String)
      */
-    void grantRuntimePermission(String packageName, String permName, int userId);
+    void grantRuntimePermission(String packageName, String permName, int deviceId,
+            @UserIdInt int userId);
 
     /**
      * Revoke a runtime permission that was previously granted by
@@ -310,13 +314,14 @@
      *
      * @param packageName the package from which to revoke the permission
      * @param permName the permission name to revoke
+     * @param deviceId the device for which to revoke the permission
      * @param userId the user for which to revoke the permission
      * @param reason the reason for the revoke, or {@code null} for unspecified
      *
-     * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+     * @see #grantRuntimePermission(String, String, int, int)
      */
-    void revokeRuntimePermission(String packageName, String permName, int userId,
-            String reason);
+    void revokeRuntimePermission(String packageName, String permName, int deviceId,
+            @UserIdInt int userId, String reason);
 
     /**
      * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
@@ -333,24 +338,29 @@
      * does not clearly communicate to the user what would be the benefit from grating this
      * permission.
      *
+     * @param packageName the package name
      * @param permName a permission your app wants to request
+     * @param deviceId the device for which to check the permission
+     * @param userId the user for which to check the permission
      * @return whether you can show permission rationale UI
      */
     boolean shouldShowRequestPermissionRationale(String packageName, String permName,
-            @UserIdInt int userId);
+            int deviceId, @UserIdInt int userId);
 
     /**
-     * Checks whether a particular permissions has been revoked for a package by policy. Typically
+     * Checks whether a particular permission has been revoked for a package by policy. Typically,
      * the device owner or the profile owner may apply such a policy. The user cannot grant policy
      * revoked permissions, hence the only way for an app to get such a permission is by a policy
      * change.
      *
      * @param packageName the name of the package you are checking against
      * @param permName the name of the permission you are checking for
-     *
+     * @param deviceId the device for which you are checking the permission
+     * @param userId the device for which you are checking the permission
      * @return whether the permission is restricted by policy
      */
-    boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId);
+    boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+            @UserIdInt int userId);
 
     /**
      * Get set of permissions that have been split into more granular or dependent permissions.
@@ -373,14 +383,25 @@
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
     /**
-     * TODO:theianchen add doc describing this is the old checkPermissionImpl
+     * Check whether a permission is granted or not to a package.
+     *
+     * @param pkgName package name
+     * @param permName permission name
+     * @param deviceId device ID
+     * @param userId user ID
+     * @return permission result {@link PackageManager.PermissionResult}
      */
-    int checkPermission(String pkgName, String permName, int userId);
+    int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId);
 
     /**
-     * TODO:theianchen add doc describing this is the old checkUidPermissionImpl
+     * Check whether a permission is granted or not to an UID.
+     *
+     * @param uid UID
+     * @param permName permission name
+     * @param deviceId device ID
+     * @return permission result {@link PackageManager.PermissionResult}
      */
-    int checkUidPermission(int uid, String permName);
+    int checkUidPermission(int uid, String permName, int deviceId);
 
     /**
      * Get all the package names requesting app op permissions.
@@ -400,15 +421,11 @@
             @UserIdInt int userId);
 
     /**
-     * Reset the runtime permission state changes for a package.
+     * Reset the runtime permission state changes for a package for all devices.
      *
      * TODO(zhanghai): Turn this into package change callback?
-     *
-     * @param pkg the package
-     * @param userId the user ID
      */
-    void resetRuntimePermissions(@NonNull AndroidPackage pkg,
-            @UserIdInt int userId);
+    void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId);
 
     /**
      * Reset the runtime permission state changes for all packages in a user.
@@ -449,8 +466,8 @@
     /**
      * Get all the permissions granted to a package.
      *
-     * @param packageName the name of the package
-     * @param userId the user ID
+     * @param packageName package name
+     * @param userId user ID
      * @return the names of the granted permissions
      */
     @NonNull
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index 7f98e21..dacb8c6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -120,21 +120,21 @@
     }
 
     @Override
-    public int getPermissionFlags(String packageName, String permName, int userId) {
+    public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
         Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
-                + permName + ", userId = " + userId + ")");
-        return mService.getPermissionFlags(packageName, permName, userId);
+                + permName + ", deviceId = " + deviceId +  ", userId = " + userId + ")");
+        return mService.getPermissionFlags(packageName, permName, deviceId, userId);
     }
 
     @Override
     public void updatePermissionFlags(String packageName, String permName, int flagMask,
-            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
         Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
                 + permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
                 + ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
-                + ", userId = " + userId + ")");
+                + ", deviceId = " + deviceId + ", userId = " + userId + ")");
         mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
-                checkAdjustPolicyFlagPermission, userId);
+                checkAdjustPolicyFlagPermission, deviceId, userId);
     }
 
     @Override
@@ -182,18 +182,20 @@
     }
 
     @Override
-    public void grantRuntimePermission(String packageName, String permName, int userId) {
+    public void grantRuntimePermission(String packageName, String permName, int deviceId,
+            int userId) {
         Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
-                + permName + ", userId = " + userId + ")");
-        mService.grantRuntimePermission(packageName, permName, userId);
+                + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+        mService.grantRuntimePermission(packageName, permName, deviceId, userId);
     }
 
     @Override
-    public void revokeRuntimePermission(String packageName, String permName, int userId,
-            String reason) {
+    public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+            int userId, String reason) {
         Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
-                + permName + ", userId = " + userId + ", reason = " + reason + ")");
-        mService.revokeRuntimePermission(packageName, permName, userId, reason);
+                + permName + ", deviceId = " + deviceId + ", userId = " + userId
+                + ", reason = " + reason + ")");
+        mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
     }
 
     @Override
@@ -205,17 +207,20 @@
 
     @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
-            int userId) {
+            int deviceId, int userId) {
         Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
-                + ", permName = " + permName + ", userId = " + userId + ")");
-        return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+                + ", permName = " + permName + ", deviceId = " + deviceId
+                +  ", userId = " + userId + ")");
+        return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
+                userId);
     }
 
     @Override
-    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+            int userId) {
         Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
-                + permName + ", userId = " + userId + ")");
-        return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+                + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+        return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
     }
 
     @Override
@@ -225,16 +230,17 @@
     }
 
     @Override
-    public int checkPermission(String pkgName, String permName, int userId) {
+    public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
         Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
-                + ", userId = " + userId + ")");
-        return mService.checkPermission(pkgName, permName, userId);
+                + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+        return mService.checkPermission(pkgName, permName, deviceId, userId);
     }
 
     @Override
-    public int checkUidPermission(int uid, String permName) {
-        Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")");
-        return mService.checkUidPermission(uid, permName);
+    public int checkUidPermission(int uid, String permName, int deviceId) {
+        Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
+                + ", deviceId = " + deviceId + ")");
+        return mService.checkUidPermission(uid, permName, deviceId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index d4c6d42..35d165b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -153,9 +153,10 @@
     }
 
     @Override
-    public int getPermissionFlags(String packageName, String permName, int userId) {
-        int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId);
-        int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId);
+    public int getPermissionFlags(String packageName, String permName, int deviceId,
+            @UserIdInt int userId) {
+        int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+        int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
 
         if (!Objects.equals(oldVal, newVal)) {
             signalImplDifference("getPermissionFlags");
@@ -165,11 +166,12 @@
 
     @Override
     public void updatePermissionFlags(String packageName, String permName, int flagMask,
-            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId,
+            @UserIdInt int userId) {
         mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
-                checkAdjustPolicyFlagPermission, userId);
+                checkAdjustPolicyFlagPermission, deviceId, userId);
         mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
-                checkAdjustPolicyFlagPermission, userId);
+                checkAdjustPolicyFlagPermission, deviceId, userId);
     }
 
     @Override
@@ -234,16 +236,17 @@
     }
 
     @Override
-    public void grantRuntimePermission(String packageName, String permName, int userId) {
-        mOldImplementation.grantRuntimePermission(packageName, permName, userId);
-        mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+    public void grantRuntimePermission(String packageName, String permName, int deviceId,
+            @UserIdInt int userId) {
+        mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+        mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
     }
 
     @Override
-    public void revokeRuntimePermission(String packageName, String permName, int userId,
-            String reason) {
-        mOldImplementation.grantRuntimePermission(packageName, permName, userId);
-        mNewImplementation.grantRuntimePermission(packageName, permName, userId);
+    public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+            @UserIdInt int userId, String reason) {
+        mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+        mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
     }
 
     @Override
@@ -255,11 +258,11 @@
 
     @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
-            int userId) {
-        boolean oldVal = mOldImplementation
-                .shouldShowRequestPermissionRationale(packageName, permName, userId);
-        boolean newVal = mNewImplementation
-                .shouldShowRequestPermissionRationale(packageName, permName, userId);
+            int deviceId, @UserIdInt int userId) {
+        boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
+                permName, deviceId,  userId);
+        boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
+                permName, deviceId, userId);
 
         if (!Objects.equals(oldVal, newVal)) {
             signalImplDifference("shouldShowRequestPermissionRationale");
@@ -268,11 +271,12 @@
     }
 
     @Override
-    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
-        boolean oldVal = mOldImplementation
-                .isPermissionRevokedByPolicy(packageName, permName, userId);
+    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+            @UserIdInt int userId) {
+        boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
+                deviceId, userId);
         boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName,
-                userId);
+                deviceId, userId);
 
         if (!Objects.equals(oldVal, newVal)) {
             signalImplDifference("isPermissionRevokedByPolicy");
@@ -292,9 +296,9 @@
     }
 
     @Override
-    public int checkPermission(String pkgName, String permName, int userId) {
-        int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId);
-        int newVal = mNewImplementation.checkPermission(pkgName, permName, userId);
+    public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+        int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
+        int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
 
         if (!Objects.equals(oldVal, newVal)) {
             signalImplDifference("checkPermission");
@@ -303,9 +307,9 @@
     }
 
     @Override
-    public int checkUidPermission(int uid, String permName) {
-        int oldVal = mOldImplementation.checkUidPermission(uid, permName);
-        int newVal = mNewImplementation.checkUidPermission(uid, permName);
+    public int checkUidPermission(int uid, String permName, int deviceId) {
+        int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
+        int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
 
         if (!Objects.equals(oldVal, newVal)) {
             signalImplDifference("checkUidPermission");
@@ -372,7 +376,7 @@
 
     @NonNull
     @Override
-    public Set<String> getGrantedPermissions(@NonNull String packageName, int userId) {
+    public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
         Set<String> oldVal = mOldImplementation.getGrantedPermissions(packageName, userId);
         Set<String> newVal = mNewImplementation.getGrantedPermissions(packageName, userId);
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index 4e72fae..cbeede0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -158,10 +158,10 @@
     }
 
     @Override
-    public int getPermissionFlags(String packageName, String permName, int userId) {
+    public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
         Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
         try {
-            return mService.getPermissionFlags(packageName, permName, userId);
+            return mService.getPermissionFlags(packageName, permName, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
@@ -169,12 +169,12 @@
 
     @Override
     public void updatePermissionFlags(String packageName, String permName, int flagMask,
-            int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
+            int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
         try {
             mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
-                    checkAdjustPolicyFlagPermission, userId);
+                    checkAdjustPolicyFlagPermission, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
@@ -253,23 +253,24 @@
     }
 
     @Override
-    public void grantRuntimePermission(String packageName, String permName, int userId) {
+    public void grantRuntimePermission(String packageName, String permName, int deviceId,
+            int userId) {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
         try {
-            mService.grantRuntimePermission(packageName, permName, userId);
+            mService.grantRuntimePermission(packageName, permName, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
     }
 
     @Override
-    public void revokeRuntimePermission(String packageName, String permName, int userId,
-            String reason) {
+    public void revokeRuntimePermission(String packageName, String permName, int deviceId,
+            int userId, String reason) {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
         try {
-            mService.revokeRuntimePermission(packageName, permName, userId, reason);
+            mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
@@ -288,22 +289,24 @@
 
     @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
-            int userId) {
+            int deviceId, int userId) {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
         try {
-            return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
+            return mService.shouldShowRequestPermissionRationale(
+                    packageName, permName, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
     }
 
     @Override
-    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+            int userId) {
         Trace.traceBegin(TRACE_TAG,
                 "TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
         try {
-            return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
+            return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
@@ -321,20 +324,20 @@
     }
 
     @Override
-    public int checkPermission(String pkgName, String permName, int userId) {
+    public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
         Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
         try {
-            return mService.checkPermission(pkgName, permName, userId);
+            return mService.checkPermission(pkgName, permName, deviceId, userId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
     }
 
     @Override
-    public int checkUidPermission(int uid, String permName) {
+    public int checkUidPermission(int uid, String permName, int deviceId) {
         Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
         try {
-            return mService.checkUidPermission(uid, permName);
+            return mService.checkUidPermission(uid, permName, deviceId);
         } finally {
             Trace.traceEnd(TRACE_TAG);
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f86f1d..faf132e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2656,7 +2656,19 @@
         }
     }
 
-    public void updateSettings() {
+    private void updateSettings() {
+        updateSettings(null);
+    }
+
+    /**
+     * Update provider Setting values on a given {@code handler}, or synchronously if {@code null}
+     * is passed for handler.
+     */
+    void updateSettings(Handler handler) {
+        if (handler != null) {
+            handler.post(() -> updateSettings(null));
+            return;
+        }
         ContentResolver resolver = mContext.getContentResolver();
         boolean updateRotation = false;
         synchronized (mLock) {
@@ -4499,6 +4511,7 @@
                 } else {
                     sleepRelease(event.getEventTime());
                 }
+                sendSystemKeyToStatusBarAsync(event);
                 break;
             }
 
@@ -4509,6 +4522,7 @@
                 if (!down) {
                     mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
                 }
+                sendSystemKeyToStatusBarAsync(event);
                 break;
             }
 
@@ -5583,12 +5597,7 @@
         mDefaultDisplayRotation.updateOrientationListener();
         synchronized (mLock) {
             mSystemReady = true;
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    updateSettings();
-                }
-            });
+            updateSettings(mHandler);
             // If this happens, for whatever reason, systemReady came later than systemBooted.
             // And keyguard should be already bound from systemBooted
             if (mSystemBooted) {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 4f7f13e..fed6e7e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.AudioAttributes;
 import android.os.CombinedVibration;
 import android.os.IBinder;
 import android.os.VibrationAttributes;
@@ -27,8 +28,10 @@
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
+import android.util.IndentingPrintWriter;
 import android.util.proto.ProtoOutputStream;
 
+import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Locale;
@@ -39,7 +42,9 @@
  * The base class for all vibrations.
  */
 abstract class Vibration {
-    private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+    private static final SimpleDateFormat DEBUG_TIME_FORMAT =
+            new SimpleDateFormat("HH:mm:ss.SSS");
+    private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT =
             new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     // Used to generate globally unique vibration ids.
     private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
@@ -146,10 +151,10 @@
         @Override
         public String toString() {
             return "CallerInfo{"
-                    + " attrs=" + attrs
-                    + ", uid=" + uid
-                    + ", displayId=" + displayId
+                    + " uid=" + uid
                     + ", opPkg=" + opPkg
+                    + ", displayId=" + displayId
+                    + ", attrs=" + attrs
                     + ", reason=" + reason
                     + '}';
         }
@@ -203,14 +208,17 @@
      * potentially expensive or resource-linked objects, such as {@link IBinder}.
      */
     static final class DebugInfo {
-        private final long mCreateTime;
+        final long mCreateTime;
+        final CallerInfo mCallerInfo;
+        @Nullable
+        final CombinedVibration mPlayedEffect;
+
         private final long mStartTime;
         private final long mEndTime;
         private final long mDurationMs;
-        @Nullable private final CombinedVibration mOriginalEffect;
-        @Nullable private final CombinedVibration mPlayedEffect;
+        @Nullable
+        private final CombinedVibration mOriginalEffect;
         private final float mScale;
-        private final CallerInfo mCallerInfo;
         private final Status mStatus;
 
         DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
@@ -230,10 +238,10 @@
 
         @Override
         public String toString() {
-            return "createTime: " + DEBUG_DATE_FORMAT.format(new Date(mCreateTime))
-                    + ", startTime: " + DEBUG_DATE_FORMAT.format(new Date(mStartTime))
+            return "createTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime))
+                    + ", startTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime))
                     + ", endTime: "
-                    + (mEndTime == 0 ? null : DEBUG_DATE_FORMAT.format(new Date(mEndTime)))
+                    + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime)))
                     + ", durationMs: " + mDurationMs
                     + ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
                     + ", playedEffect: " + mPlayedEffect
@@ -242,8 +250,56 @@
                     + ", callerInfo: " + mCallerInfo;
         }
 
+        /**
+         * Write this info in a compact way into given {@link PrintWriter}.
+         *
+         * <p>This is used by dumpsys to log multiple vibration records in single lines that are
+         * easy to skim through by the sorted created time.
+         */
+        void dumpCompact(IndentingPrintWriter pw) {
+            boolean isExternalVibration = mPlayedEffect == null;
+            String timingsStr = String.format(Locale.ROOT,
+                    "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s",
+                    DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
+                    isExternalVibration ? "external" : "effect",
+                    mStatus.name().toLowerCase(Locale.ROOT),
+                    mDurationMs,
+                    mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
+                    mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
+            String callerInfoStr = String.format(Locale.ROOT,
+                    " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+                    mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+                    mCallerInfo.attrs.usageToString(),
+                    AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
+                    Long.toBinaryString(mCallerInfo.attrs.getFlags()),
+                    mCallerInfo.reason);
+            String effectStr = String.format(Locale.ROOT,
+                    " | played: %s | original: %s | scale: %.2f",
+                    mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
+                    mOriginalEffect == null ? null : mOriginalEffect.toDebugString(),
+                    mScale);
+            pw.println(timingsStr + callerInfoStr + effectStr);
+        }
+
+        /** Write this info into given {@link PrintWriter}. */
+        void dump(IndentingPrintWriter pw) {
+            pw.println("Vibration:");
+            pw.increaseIndent();
+            pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
+            pw.println("durationMs = " + mDurationMs);
+            pw.println("createTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)));
+            pw.println("startTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime)));
+            pw.println("endTime = "
+                    + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime))));
+            pw.println("playedEffect = " + mPlayedEffect);
+            pw.println("originalEffect = " + mOriginalEffect);
+            pw.println("scale = " + String.format(Locale.ROOT, "%.2f", mScale));
+            pw.println("callerInfo = " + mCallerInfo);
+            pw.decreaseIndent();
+        }
+
         /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
-        public void dumpProto(ProtoOutputStream proto, long fieldId) {
+        void dump(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
             proto.write(VibrationProto.START_TIME, mStartTime);
             proto.write(VibrationProto.END_TIME, mEndTime);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index dbd6bf4..db8a9ae 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -54,6 +54,7 @@
 import android.os.Vibrator.VibrationIntensity;
 import android.os.vibrator.VibrationConfig;
 import android.provider.Settings;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -65,6 +66,7 @@
 import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
@@ -601,8 +603,32 @@
         }
     }
 
+    /** Write current settings into given {@link PrintWriter}. */
+    void dump(IndentingPrintWriter pw) {
+        pw.println("VibrationSettings:");
+        pw.increaseIndent();
+        pw.println("vibrateOn = " + mVibrateOn);
+        pw.println("vibrateInputDevices = " + mVibrateInputDevices);
+        pw.println("batterySaverMode = " + mBatterySaverMode);
+        pw.println("VibrationIntensities:");
+
+        pw.increaseIndent();
+        for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+            int usage = mCurrentVibrationIntensities.keyAt(i);
+            int intensity = mCurrentVibrationIntensities.valueAt(i);
+            pw.println(VibrationAttributes.usageToString(usage) + " = "
+                    + intensityToString(intensity)
+                    + ", default: " + intensityToString(getDefaultIntensity(usage)));
+        }
+        pw.decreaseIndent();
+
+        mVibrationConfig.dumpWithoutDefaultSettings(pw);
+        pw.println("processStateCache = " + mUidObserver.mProcStatesCache);
+        pw.decreaseIndent();
+    }
+
     /** Write current settings into given {@link ProtoOutputStream}. */
-    public void dumpProto(ProtoOutputStream proto) {
+    void dump(ProtoOutputStream proto) {
         synchronized (mLock) {
             proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
             proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 47b3e1a..f5d4d1e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -17,6 +17,7 @@
 package com.android.server.vibrator;
 
 import android.annotation.Nullable;
+import android.annotation.Nullable;
 import android.hardware.vibrator.IVibrator;
 import android.os.Binder;
 import android.os.IVibratorStateListener;
@@ -26,6 +27,7 @@
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.RampSegment;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -351,6 +353,19 @@
                 + '}';
     }
 
+    void dump(IndentingPrintWriter pw) {
+        pw.println("VibratorController:");
+        pw.increaseIndent();
+        pw.println("isVibrating = " + mIsVibrating);
+        pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
+        pw.println("currentAmplitude = " + mCurrentAmplitude);
+        pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
+        pw.println("vibratorStateListenerCount = "
+                + mVibratorStateListeners.getRegisteredCallbackCount());
+        mVibratorInfo.dump(pw);
+        pw.decreaseIndent();
+    }
+
     @GuardedBy("mLock")
     private void notifyListenerOnVibrating(boolean isVibrating) {
         if (mIsVibrating != isVibrating) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 6fdb1db..270f7f0c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -56,6 +56,7 @@
 import android.os.vibrator.VibrationEffectSegment;
 import android.os.vibrator.persistence.VibrationXmlParser;
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -80,6 +81,7 @@
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -204,9 +206,15 @@
         mNativeWrapper = injector.getNativeWrapper();
         mNativeWrapper.init(listener);
 
-        int dumpLimit = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
-        mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+        int recentDumpSizeLimit = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_recentVibrationsDumpSizeLimit);
+        int dumpSizeLimit = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
+        int dumpAggregationTimeLimit = mContext.getResources().getInteger(
+                com.android.internal.R.integer
+                        .config_previousVibrationsDumpAggregationTimeMillisLimit);
+        mVibratorManagerRecords = new VibratorManagerRecords(
+                recentDumpSizeLimit, dumpSizeLimit, dumpAggregationTimeLimit);
 
         mBatteryStatsService = injector.getBatteryStatsService();
         mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
@@ -544,49 +552,74 @@
         }
     }
 
-    private void dumpText(PrintWriter pw) {
+    private void dumpText(PrintWriter w) {
         if (DEBUG) {
             Slog.d(TAG, "Dumping vibrator manager service to text...");
         }
+        IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ "  ");
         synchronized (mLock) {
             pw.println("Vibrator Manager Service:");
-            pw.println("  mVibrationSettings:");
-            pw.println("    " + mVibrationSettings);
+            pw.increaseIndent();
+
+            mVibrationSettings.dump(pw);
             pw.println();
-            pw.println("  mVibratorControllers:");
+
+            pw.println("VibratorControllers:");
+            pw.increaseIndent();
             for (int i = 0; i < mVibrators.size(); i++) {
-                pw.println("    " + mVibrators.valueAt(i));
+                mVibrators.valueAt(i).dump(pw);
             }
+            pw.decreaseIndent();
             pw.println();
-            pw.println("  mCurrentVibration:");
-            pw.println("    " + (mCurrentVibration == null
-                    ? null : mCurrentVibration.getVibration().getDebugInfo()));
+
+            pw.println("CurrentVibration:");
+            pw.increaseIndent();
+            if (mCurrentVibration != null) {
+                mCurrentVibration.getVibration().getDebugInfo().dump(pw);
+            } else {
+                pw.println("null");
+            }
+            pw.decreaseIndent();
             pw.println();
-            pw.println("  mNextVibration:");
-            pw.println("    " + (mNextVibration == null
-                    ? null : mNextVibration.getVibration().getDebugInfo()));
+
+            pw.println("NextVibration:");
+            pw.increaseIndent();
+            if (mNextVibration != null) {
+                mNextVibration.getVibration().getDebugInfo().dump(pw);
+            } else {
+                pw.println("null");
+            }
+            pw.decreaseIndent();
             pw.println();
-            pw.println("  mCurrentExternalVibration:");
-            pw.println("    " + (mCurrentExternalVibration == null
-                    ? null : mCurrentExternalVibration.getDebugInfo()));
-            pw.println();
+
+            pw.println("CurrentExternalVibration:");
+            pw.increaseIndent();
+            if (mCurrentExternalVibration != null) {
+                mCurrentExternalVibration.getDebugInfo().dump(pw);
+            } else {
+                pw.println("null");
+            }
+            pw.decreaseIndent();
         }
-        mVibratorManagerRecords.dumpText(pw);
+
+        pw.println();
+        pw.println();
+        mVibratorManagerRecords.dump(pw);
     }
 
-    synchronized void dumpProto(FileDescriptor fd) {
+    private void dumpProto(FileDescriptor fd) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
         if (DEBUG) {
             Slog.d(TAG, "Dumping vibrator manager service to proto...");
         }
         synchronized (mLock) {
-            mVibrationSettings.dumpProto(proto);
+            mVibrationSettings.dump(proto);
             if (mCurrentVibration != null) {
-                mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+                mCurrentVibration.getVibration().getDebugInfo().dump(proto,
                         VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
             }
             if (mCurrentExternalVibration != null) {
-                mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+                mCurrentExternalVibration.getDebugInfo().dump(proto,
                         VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
             }
 
@@ -601,7 +634,7 @@
             proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
                     isUnderExternalControl);
         }
-        mVibratorManagerRecords.dumpProto(proto);
+        mVibratorManagerRecords.dump(proto);
         proto.flush();
     }
 
@@ -1581,64 +1614,105 @@
 
     /** Keep records of vibrations played and provide debug information for this service. */
     private static final class VibratorManagerRecords {
-        private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
-                new SparseArray<>();
-        private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
-                new LinkedList<>();
-        private final int mPreviousVibrationsLimit;
+        private final VibrationRecords mAggregatedVibrationHistory;
+        private final VibrationRecords mRecentVibrations;
 
-        VibratorManagerRecords(int limit) {
-            mPreviousVibrationsLimit = limit;
+        VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit,
+                int aggregationTimeLimit) {
+            mAggregatedVibrationHistory =
+                    new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
+            mRecentVibrations = new VibrationRecords(
+                    recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
         }
 
         synchronized void record(HalVibration vib) {
-            int usage = vib.callerInfo.attrs.getUsage();
-            if (!mPreviousVibrations.contains(usage)) {
-                mPreviousVibrations.put(usage, new LinkedList<>());
-            }
-            record(mPreviousVibrations.get(usage), vib.getDebugInfo());
+            record(vib.getDebugInfo());
         }
 
         synchronized void record(ExternalVibrationHolder vib) {
-            record(mPreviousExternalVibrations, vib.getDebugInfo());
+            record(vib.getDebugInfo());
         }
 
-        synchronized void record(LinkedList<Vibration.DebugInfo> records,
-                Vibration.DebugInfo info) {
-            if (records.size() > mPreviousVibrationsLimit) {
-                records.removeFirst();
+        private synchronized void record(Vibration.DebugInfo info) {
+            AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info);
+            if (removedRecord != null) {
+                mAggregatedVibrationHistory.record(removedRecord.mLatestVibration);
             }
-            records.addLast(info);
         }
 
-        synchronized void dumpText(PrintWriter pw) {
-            for (int i = 0; i < mPreviousVibrations.size(); i++) {
-                pw.println();
-                pw.print("  Previous vibrations for usage ");
-                pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
-                pw.println(":");
-                for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
-                    pw.println("    " + info);
+        synchronized void dump(IndentingPrintWriter pw) {
+            pw.println("Recent vibrations:");
+            pw.increaseIndent();
+            mRecentVibrations.dump(pw);
+            pw.decreaseIndent();
+            pw.println();
+            pw.println();
+
+            pw.println("Aggregated vibration history:");
+            pw.increaseIndent();
+            mAggregatedVibrationHistory.dump(pw);
+            pw.decreaseIndent();
+        }
+
+        synchronized void dump(ProtoOutputStream proto) {
+            mRecentVibrations.dump(proto);
+        }
+    }
+
+    /** Keep records of vibrations played and provide debug information for this service. */
+    private static final class VibrationRecords {
+        private final SparseArray<LinkedList<AggregatedVibrationRecord>> mVibrations =
+                new SparseArray<>();
+        private final int mSizeLimit;
+        private final int mAggregationTimeLimit;
+
+        VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
+            mSizeLimit = sizeLimit;
+            mAggregationTimeLimit = aggregationTimeLimit;
+        }
+
+        synchronized AggregatedVibrationRecord record(Vibration.DebugInfo info) {
+            int usage = info.mCallerInfo.attrs.getUsage();
+            if (!mVibrations.contains(usage)) {
+                mVibrations.put(usage, new LinkedList<>());
+            }
+            LinkedList<AggregatedVibrationRecord> records = mVibrations.get(usage);
+            if (mAggregationTimeLimit > 0 && !records.isEmpty()) {
+                AggregatedVibrationRecord lastRecord = records.getLast();
+                if (lastRecord.mayAggregate(info, mAggregationTimeLimit)) {
+                    lastRecord.record(info);
+                    return null;
                 }
             }
+            AggregatedVibrationRecord removedRecord = null;
+            if (records.size() > mSizeLimit) {
+                removedRecord = records.removeFirst();
+            }
+            records.addLast(new AggregatedVibrationRecord(info));
+            return removedRecord;
+        }
 
-            pw.println();
-            pw.println("  Previous external vibrations:");
-            for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
-                pw.println("    " + info);
+        synchronized void dump(IndentingPrintWriter pw) {
+            for (int i = 0; i < mVibrations.size(); i++) {
+                pw.println(VibrationAttributes.usageToString(mVibrations.keyAt(i)) + ":");
+                pw.increaseIndent();
+                for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+                    info.dump(pw);
+                }
+                pw.decreaseIndent();
+                pw.println();
             }
         }
 
-        synchronized void dumpProto(ProtoOutputStream proto) {
-            for (int i = 0; i < mPreviousVibrations.size(); i++) {
+        synchronized void dump(ProtoOutputStream proto) {
+            for (int i = 0; i < mVibrations.size(); i++) {
                 long fieldId;
-                switch (mPreviousVibrations.keyAt(i)) {
+                switch (mVibrations.keyAt(i)) {
                     case VibrationAttributes.USAGE_RINGTONE:
                         fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
                         break;
                     case VibrationAttributes.USAGE_NOTIFICATION:
-                        fieldId = VibratorManagerServiceDumpProto
-                                .PREVIOUS_NOTIFICATION_VIBRATIONS;
+                        fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
                         break;
                     case VibrationAttributes.USAGE_ALARM:
                         fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
@@ -1646,18 +1720,70 @@
                     default:
                         fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
                 }
-                for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
-                    info.dumpProto(proto, fieldId);
+                for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+                    if (info.mLatestVibration.mPlayedEffect == null) {
+                        // External vibrations are reported separately in the dump proto
+                        info.dump(proto,
+                                VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+                    } else {
+                        info.dump(proto, fieldId);
+                    }
                 }
             }
+        }
 
-            for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
-                info.dumpProto(proto,
-                        VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+        synchronized void dumpOnSingleField(ProtoOutputStream proto, long fieldId) {
+            for (int i = 0; i < mVibrations.size(); i++) {
+                for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+                    info.dump(proto, fieldId);
+                }
             }
         }
     }
 
+    /**
+     * Record that keeps the last {@link Vibration.DebugInfo} played, aggregating close vibrations
+     * from the same uid that have the same {@link VibrationAttributes} and {@link VibrationEffect}.
+     */
+    private static final class AggregatedVibrationRecord {
+        private final Vibration.DebugInfo mFirstVibration;
+        private Vibration.DebugInfo mLatestVibration;
+        private int mVibrationCount;
+
+        AggregatedVibrationRecord(Vibration.DebugInfo info) {
+            mLatestVibration = mFirstVibration = info;
+            mVibrationCount = 1;
+        }
+
+        synchronized boolean mayAggregate(Vibration.DebugInfo info, long timeLimit) {
+            return Objects.equals(mLatestVibration.mCallerInfo.uid, info.mCallerInfo.uid)
+                    && Objects.equals(mLatestVibration.mCallerInfo.attrs, info.mCallerInfo.attrs)
+                    && Objects.equals(mLatestVibration.mPlayedEffect, info.mPlayedEffect)
+                    && Math.abs(mLatestVibration.mCreateTime - info.mCreateTime) < timeLimit;
+        }
+
+        synchronized void record(Vibration.DebugInfo vib) {
+            mLatestVibration = vib;
+            mVibrationCount++;
+        }
+
+        synchronized void dump(IndentingPrintWriter pw) {
+            mFirstVibration.dumpCompact(pw);
+            if (mVibrationCount == 1) {
+                return;
+            }
+            if (mVibrationCount > 2) {
+                pw.println(
+                        "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:");
+            }
+            mLatestVibration.dumpCompact(pw);
+        }
+
+        synchronized void dump(ProtoOutputStream proto, long fieldId) {
+            mLatestVibration.dump(proto, fieldId);
+        }
+    }
+
     /** Clears mNextVibration if set, ending it cleanly */
     @GuardedBy("mLock")
     private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
@@ -1676,7 +1802,7 @@
     /**
      * Ends the external vibration, and clears related service state.
      *
-     * @param vibrationEndInfo the status and related info to end the associated Vibration with
+     * @param vibrationEndInfo        the status and related info to end the associated Vibration
      * @param continueExternalControl indicates whether external control will continue. If not, the
      *                                HAL will have external control turned off.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ea06b42..a7849c1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -226,6 +226,7 @@
 import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
 import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
@@ -2682,7 +2683,9 @@
     private boolean transferSplashScreenIfNeeded() {
         if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
                 || mStartingWindow == null
-                || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
+                || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
+                // skip copy splash screen to client if it was resized
+                || (mStartingData != null && mStartingData.mResizedFromTransfer)) {
             return false;
         }
         if (isTransferringSplashScreen()) {
@@ -7644,7 +7647,8 @@
     @Override
     void prepareSurfaces() {
         final boolean show = isVisible() || isAnimating(PARENTS,
-                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
+                        | ANIMATION_TYPE_PREDICT_BACK);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0c196d7..976641b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -26,7 +26,9 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
 import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS;
+import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING;
 import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE;
+import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY;
 import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
 
@@ -50,6 +52,7 @@
 import android.window.BackAnimationAdapter;
 import android.window.BackNavigationInfo;
 import android.window.IBackAnimationFinishedCallback;
+import android.window.IWindowlessStartingSurfaceCallback;
 import android.window.OnBackInvokedCallbackInfo;
 import android.window.TaskSnapshot;
 
@@ -73,6 +76,8 @@
     private @BackNavigationInfo.BackTargetType int mLastBackType;
     private boolean mShowWallpaper;
     private Runnable mPendingAnimation;
+
+    private boolean mBackAnimationRunning;
     private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
 
     private AnimationHandler mAnimationHandler;
@@ -474,7 +479,7 @@
                 final ActivityRecord ar = openApps.valueAt(i);
                 if (mAnimationHandler.isTarget(ar, true /* open */)) {
                     openApps.removeAt(i);
-                    mAnimationHandler.mOpenTransitionTargetMatch = true;
+                    mAnimationHandler.markStartingSurfaceMatch();
                 }
             }
             for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -583,8 +588,9 @@
      * The closing target should only exist in close list, but the opening target can be either in
      * open or close list.
      */
-    void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets) {
-        if (!isMonitoringTransition()) {
+    void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets,
+            SurfaceControl.Transaction startTransaction) {
+        if (!isMonitoringTransition() || targets.isEmpty()) {
             return;
         }
         for (int i = targets.size() - 1; i >= 0; --i) {
@@ -613,6 +619,17 @@
                 Slog.e(TAG, "Gesture animation is applied on another transition?");
             }
             mWaitTransitionFinish = transition;
+            // Flag target matches to defer remove the splash screen.
+            for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = mTmpOpenApps.get(i);
+                if (mAnimationHandler.isTarget(wc, true /* open */)) {
+                    mAnimationHandler.markStartingSurfaceMatch();
+                    break;
+                }
+            }
+            // Because the target will reparent to transition root, so it cannot be controlled by
+            // animation leash. Hide the close target when transition starts.
+            startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
         }
         mTmpOpenApps.clear();
         mTmpCloseApps.clear();
@@ -633,6 +650,7 @@
         mAnimationHandler.clearBackAnimateTarget();
         mNavigationMonitor.stopMonitorTransition();
         mWaitTransitionFinish = null;
+        mBackAnimationRunning = false;
     }
 
     /**
@@ -717,11 +735,7 @@
         // This will be set before transition happen, to know whether the real opening target
         // exactly match animating target. When target match, reparent the starting surface to
         // the opening target like starting window do.
-        private boolean mOpenTransitionTargetMatch;
-        // The starting surface task Id. Used to clear the starting surface if the animation has
-        // request one during animating.
-        private int mRequestedStartingSurfaceTaskId;
-        private SurfaceControl mStartingSurface;
+        private boolean mStartingSurfaceTargetMatch;
         private ActivityRecord mOpenActivity;
 
         AnimationHandler(WindowManagerService wms) {
@@ -765,8 +779,8 @@
                 return;
             }
 
-            mCloseAdaptor = createAdaptor(closeTarget, false /* isOpen */);
-            mOpenAdaptor = createAdaptor(open, true /* isOpen */);
+            mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType);
+            mOpenAdaptor = createAdaptor(open, true, mSwitchType);
             mOpenActivity = openActivity;
             if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) {
                 Slog.w(TAG, "composeNewAnimations fail, skip");
@@ -774,8 +788,8 @@
             }
         }
 
-        boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open,
-                ActivityRecord openActivity) {
+        private boolean composeAnimations(@NonNull WindowContainer close,
+                @NonNull WindowContainer open, ActivityRecord openActivity) {
             if (mComposed || mWaitTransition) {
                 Slog.e(TAG, "Previous animation is running " + this);
                 return false;
@@ -805,28 +819,6 @@
                     .isSupportWindowlessStartingSurface();
         }
 
-        void createStartingSurface(TaskSnapshot snapshot) {
-            if (!mComposed) {
-                return;
-            }
-
-            final ActivityRecord topActivity = getTopOpenActivity();
-            if (topActivity == null) {
-                Slog.e(TAG, "createStartingSurface fail, no open activity: " + this);
-                return;
-            }
-            // TODO (b/257857570) draw snapshot by starting surface.
-        }
-
-        private ActivityRecord getTopOpenActivity() {
-            if (mSwitchType == ACTIVITY_SWITCH) {
-                return mOpenAdaptor.mTarget.asActivityRecord();
-            } else if (mSwitchType == TASK_SWITCH) {
-                return mOpenAdaptor.mTarget.asTask().getTopNonFinishingActivity();
-            }
-            return null;
-        }
-
         boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) {
             for (int i = wcs.size() - 1; i >= 0; --i) {
                 if (isTarget(wcs.get(i), open)) {
@@ -860,13 +852,13 @@
             if (!mComposed) {
                 return;
             }
-            cleanUpWindowlessSurface();
 
             if (mCloseAdaptor != null) {
                 mCloseAdaptor.mTarget.cancelAnimation();
                 mCloseAdaptor = null;
             }
             if (mOpenAdaptor != null) {
+                mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch);
                 mOpenAdaptor.mTarget.cancelAnimation();
                 mOpenAdaptor = null;
             }
@@ -875,36 +867,16 @@
             }
         }
 
-        private void cleanUpWindowlessSurface() {
-            final ActivityRecord ar = getTopOpenActivity();
-            if (ar == null) {
-                Slog.w(TAG, "finishPresentAnimations without top activity: " + this);
-            }
-            final SurfaceControl.Transaction pendingT = ar != null ? ar.getPendingTransaction()
-                    : mOpenAdaptor.mTarget.getPendingTransaction();
-            // ensure open target is visible before cancel animation.
-            mOpenTransitionTargetMatch &= ar != null;
-            if (mOpenTransitionTargetMatch) {
-                pendingT.show(ar.getSurfaceControl());
-            }
-            if (mRequestedStartingSurfaceTaskId != 0) {
-                // If open target match, reparent to open activity
-                if (mStartingSurface != null && mOpenTransitionTargetMatch) {
-                    pendingT.reparent(mStartingSurface, ar.getSurfaceControl());
-                }
-                // remove starting surface.
-                mStartingSurface = null;
-                // TODO (b/257857570) draw snapshot by starting surface.
-                mRequestedStartingSurfaceTaskId = 0;
-            }
+        void markStartingSurfaceMatch() {
+            mStartingSurfaceTargetMatch = true;
+            mOpenAdaptor.reparentWindowlessSurfaceToTarget();
         }
 
         void clearBackAnimateTarget() {
             finishPresentAnimations();
             mComposed = false;
             mWaitTransition = false;
-            mOpenTransitionTargetMatch = false;
-            mRequestedStartingSurfaceTaskId = 0;
+            mStartingSurfaceTargetMatch = false;
             mSwitchType = UNKNOWN;
             mOpenActivity = null;
         }
@@ -935,9 +907,9 @@
         }
 
         private static BackWindowAnimationAdaptor createAdaptor(
-                WindowContainer target, boolean isOpen) {
+                WindowContainer target, boolean isOpen, int switchType) {
             final BackWindowAnimationAdaptor adaptor =
-                    new BackWindowAnimationAdaptor(target, isOpen);
+                    new BackWindowAnimationAdaptor(target, isOpen, switchType);
             final SurfaceControl.Transaction pt = target.getPendingTransaction();
             target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK);
             // Workaround to show TaskFragment which can be hide in Transitions and won't show
@@ -957,11 +929,19 @@
             private final WindowContainer mTarget;
             private final boolean mIsOpen;
             private RemoteAnimationTarget mAnimationTarget;
+            private final int mSwitchType;
 
-            BackWindowAnimationAdaptor(WindowContainer closeTarget, boolean isOpen) {
-                mBounds.set(closeTarget.getBounds());
-                mTarget = closeTarget;
+            // The starting surface task Id. Used to clear the starting surface if the animation has
+            // requested one during animating.
+            private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
+            private SurfaceControl mStartingSurface;
+
+            BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen,
+                    int switchType) {
+                mBounds.set(target.getBounds());
+                mTarget = target;
                 mIsOpen = isOpen;
+                mSwitchType = switchType;
             }
             @Override
             public boolean getShowWallpaper() {
@@ -979,6 +959,8 @@
             public void onAnimationCancelled(SurfaceControl animationLeash) {
                 if (mCapturedLeash == animationLeash) {
                     mCapturedLeash = null;
+                    mRequestedStartingSurfaceId = INVALID_TASK_ID;
+                    mStartingSurface = null;
                 }
             }
 
@@ -1009,8 +991,15 @@
                     return mAnimationTarget;
                 }
                 Task t = mTarget.asTask();
-                final ActivityRecord r = t != null ? t.getTopNonFinishingActivity()
-                        : mTarget.asActivityRecord();
+                ActivityRecord r = null;
+                if (t == null && mTarget.asTaskFragment() != null) {
+                    t = mTarget.asTaskFragment().getTask();
+                    r = mTarget.asTaskFragment().getTopNonFinishingActivity();
+                }
+                if (r == null) {
+                    r = t != null ? t.getTopNonFinishingActivity()
+                            : mTarget.asActivityRecord();
+                }
                 if (t == null && r != null) {
                     t = r.getTask();
                 }
@@ -1037,6 +1026,77 @@
                         r.checkEnterPictureInPictureAppOpsState());
                 return mAnimationTarget;
             }
+
+            void createStartingSurface() {
+                if (!mIsOpen) {
+                    return;
+                }
+                final Task openTask = mSwitchType == TASK_SWITCH
+                        ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH
+                        ? mTarget.asActivityRecord().getTask() : null;
+                if (openTask == null) {
+                    return;
+                }
+                final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH
+                        ? mTarget.asActivityRecord()
+                        : openTask.getTopNonFinishingActivity();
+                if (mainActivity == null) {
+                    return;
+                }
+                final TaskSnapshot snapshot = getSnapshot(mTarget);
+                mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
+                        .addWindowlessStartingSurface(openTask, mainActivity,
+                                mAnimationTarget.leash, snapshot,
+                                new IWindowlessStartingSurfaceCallback.Stub() {
+                            // Once the starting surface has been created in shell, it will call
+                            // onSurfaceAdded to pass the created surface to core, so if a
+                            // transition is triggered by the back gesture, there doesn't need to
+                            // create another starting surface for the opening target, just reparent
+                            // the starting surface to the opening target.
+                            // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
+                            // called, there won't be able to reparent the starting surface on
+                            // opening target. But if that happens and transition target is matched,
+                            // the app window should already draw.
+                                    @Override
+                                    public void onSurfaceAdded(SurfaceControl sc) {
+                                        synchronized (mTarget.mWmService.mGlobalLock) {
+                                            if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
+                                                mStartingSurface = sc;
+                                            }
+                                        }
+                                    }
+                                });
+            }
+
+            // When back gesture has triggered and transition target matches navigation target,
+            // reparent the starting surface to the opening target as it's starting window.
+            void reparentWindowlessSurfaceToTarget() {
+                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+                    return;
+                }
+                // If open target matches, reparent to open activity or task
+                if (mStartingSurface != null && mStartingSurface.isValid()) {
+                    mTarget.getPendingTransaction()
+                            .reparent(mStartingSurface, mTarget.getSurfaceControl());
+                    // remove starting surface.
+                    mStartingSurface = null;
+                }
+            }
+
+            /**
+             * Ask shell to clear the starting surface.
+             * @param openTransitionMatch if true, shell will play the remove starting window
+             *                            animation, otherwise remove it directly.
+             */
+            void cleanUpWindowlessSurface(boolean openTransitionMatch) {
+                if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+                    return;
+                }
+                mTarget.mWmService.mAtmService.mTaskOrganizerController
+                        .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
+                                !openTransitionMatch);
+                mRequestedStartingSurfaceId = INVALID_TASK_ID;
+            }
         }
 
         ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter,
@@ -1089,15 +1149,13 @@
 
             /**
              * Apply preview strategy on the opening target
-             * @param open The opening target.
+             * @param openAnimationAdaptor The animator who can create starting surface.
              * @param visibleOpenActivity  The visible activity in opening target.
-             * @return If the preview strategy is launch behind, returns the Activity that has
-             *         launchBehind set, or null otherwise.
              */
-            private void applyPreviewStrategy(WindowContainer open,
+            private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor,
                     ActivityRecord visibleOpenActivity) {
                 if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
-                    createStartingSurface(getSnapshot(open));
+                    openAnimationAdaptor.createStartingSurface();
                     return;
                 }
                 setLaunchBehind(visibleOpenActivity);
@@ -1119,7 +1177,7 @@
                 if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) {
                     return null;
                 }
-                applyPreviewStrategy(mOpenTarget, openActivity);
+                applyPreviewStrategy(mOpenAdaptor, openActivity);
 
                 final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
                 final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1220,6 +1278,7 @@
         if (mPendingAnimation != null) {
             mPendingAnimation.run();
             mPendingAnimation = null;
+            mBackAnimationRunning = true;
         }
     }
 
@@ -1236,9 +1295,6 @@
     }
 
     static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
-        if (!isScreenshotEnabled()) {
-            return null;
-        }
         if (w.asTask() != null) {
             final Task task = w.asTask();
             return  task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
@@ -1247,8 +1303,8 @@
         }
 
         if (w.asActivityRecord() != null) {
-            // TODO (b/259497289) return TaskSnapshot when feature complete.
-            return null;
+            final ActivityRecord ar = w.asActivityRecord();
+            return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar);
         }
         return null;
     }
@@ -1270,6 +1326,12 @@
         proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress);
         proto.write(LAST_BACK_TYPE, mLastBackType);
         proto.write(SHOW_WALLPAPER, mShowWallpaper);
+        if (mAnimationHandler.mOpenActivity != null) {
+            mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY);
+        } else {
+            proto.write(MAIN_OPEN_ACTIVITY, "");
+        }
+        proto.write(ANIMATION_RUNNING, mBackAnimationRunning);
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index cff86ad..2b22d75 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -38,6 +38,10 @@
      */
     Task mAssociatedTask;
 
+
+    /** Whether the starting window is resized from transfer across activities. */
+    boolean mResizedFromTransfer;
+
     /** Whether the starting window is drawn. */
     boolean mIsDisplayed;
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index cdb4ad6..b72d027 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -790,12 +790,8 @@
     }
 
     boolean isSupportWindowlessStartingSurface() {
-        // Enable after ag/20426257
         final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
-        if (lastOrganizer == null) {
-            return false;
-        }
-        return false;
+        return lastOrganizer != null;
     }
     /**
      * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 789e3d2..7a904f8 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1499,7 +1499,8 @@
         mTargets = calculateTargets(mParticipants, mChanges);
 
         // Check whether the participants were animated from back navigation.
-        mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets);
+        mController.mAtm.mBackNavigationController.onTransactionReady(this, mTargets,
+                transaction);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
         info.setDebugId(mSyncId);
         mController.assignTrack(this, info);
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 26aab07..726ae5c 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -76,7 +76,7 @@
             @NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
             @WindowType int type, @Nullable Bundle options) {
         registerWindowContainerListener(wpc, clientToken, container, type, options,
-                true /* shouDispatchConfigWhenRegistering */);
+                true /* shouldDispatchConfigWhenRegistering */);
     }
 
     /**
@@ -91,19 +91,19 @@
      * @param container the {@link WindowContainer} which the listener is going to listen to.
      * @param type the window type
      * @param options a bundle used to pass window-related options.
-     * @param shouDispatchConfigWhenRegistering {@code true} to indicate the current
+     * @param shouldDispatchConfigWhenRegistering {@code true} to indicate the current
      *                {@code container}'s config will dispatch to the client side when
      *                registering the {@link WindowContextListenerImpl}
      */
     void registerWindowContainerListener(@NonNull WindowProcessController wpc,
             @NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
             @WindowType int type, @Nullable Bundle options,
-            boolean shouDispatchConfigWhenRegistering) {
+            boolean shouldDispatchConfigWhenRegistering) {
         WindowContextListenerImpl listener = mListeners.get(clientToken);
         if (listener == null) {
             listener = new WindowContextListenerImpl(wpc, clientToken, container, type,
                     options);
-            listener.register(shouDispatchConfigWhenRegistering);
+            listener.register(shouldDispatchConfigWhenRegistering);
         } else {
             updateContainerForWindowContextListener(clientToken, container);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 210378f..261d6bc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -305,6 +305,7 @@
 import android.window.ScreenCapture;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
+import android.window.WindowContextInfo;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -2737,7 +2738,7 @@
 
     @Nullable
     @Override
-    public Configuration attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
+    public WindowContextInfo attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
             @NonNull IBinder clientToken, @LayoutParams.WindowType int type, int displayId,
             @Nullable Bundle options) {
         Objects.requireNonNull(appThread);
@@ -2766,8 +2767,8 @@
                 final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
                         callerCanManageAppTokens, false /* roundedCornerOverlay */);
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
-                        da, type, options, false /* shouDispatchConfigWhenRegistering */);
-                return da.getConfiguration();
+                        da, type, options, false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(da.getConfiguration(), displayId);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2776,8 +2777,8 @@
 
     @Nullable
     @Override
-    public Configuration attachWindowContextToDisplayContent(@NonNull IApplicationThread appThread,
-            @NonNull IBinder clientToken, int displayId) {
+    public WindowContextInfo attachWindowContextToDisplayContent(
+            @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
         Objects.requireNonNull(appThread);
         Objects.requireNonNull(clientToken);
         final int callingPid = Binder.getCallingPid();
@@ -2809,16 +2810,17 @@
 
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                         dc, INVALID_WINDOW_TYPE, null /* options */,
-                        false /* shouDispatchConfigWhenRegistering */);
-                return dc.getConfiguration();
+                        false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(dc.getConfiguration(), displayId);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
+    @Nullable
     @Override
-    public void attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
+    public WindowContextInfo attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
             @NonNull IBinder clientToken, @NonNull IBinder token) {
         Objects.requireNonNull(appThread);
         Objects.requireNonNull(clientToken);
@@ -2834,13 +2836,13 @@
                 if (wpc == null) {
                     ProtoLog.w(WM_ERROR, "attachWindowContextToWindowToken: calling from"
                             + " non-existing process pid=%d uid=%d", callingPid, callingUid);
-                    return;
+                    return null;
                 }
                 final WindowToken windowToken = mRoot.getWindowToken(token);
                 if (windowToken == null) {
                     ProtoLog.w(WM_ERROR, "Then token:%s is invalid. It might be "
                             + "removed", token);
-                    return;
+                    return null;
                 }
                 final int type = mWindowContextListenerController.getWindowType(clientToken);
                 if (type == INVALID_WINDOW_TYPE) {
@@ -2854,10 +2856,13 @@
                 }
                 if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken,
                         callerCanManageAppTokens, callingUid)) {
-                    return;
+                    return null;
                 }
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
-                        windowToken, windowToken.windowType, windowToken.mOptions);
+                        windowToken, windowToken.windowType, windowToken.mOptions,
+                                               false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(windowToken.getConfiguration(),
+                        windowToken.getDisplayContent().getDisplayId());
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index baf97b7..6d0fd2a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1935,7 +1935,9 @@
         }
         final ActivityRecord atoken = mActivityRecord;
         if (atoken != null) {
-            return ((!isParentWindowHidden() && atoken.isVisible())
+            final boolean isVisible = isStartingWindowAssociatedToTask()
+                    ? mStartingData.mAssociatedTask.isVisible() : atoken.isVisible();
+            return ((!isParentWindowHidden() && isVisible)
                     || isAnimationRunningSelfOrParent());
         }
         final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
@@ -2330,6 +2332,13 @@
             // IME surface association. (e.g. Attach IME surface on the display instead of the
             // app when the app bounds being letterboxed.)
             mDisplayContent.updateImeControlTarget(isImeLayeringTarget() /* updateImeParent */);
+            // Fix the starting window to task when Activity has changed.
+            if (mStartingData != null && mStartingData.mAssociatedTask == null
+                    && !mTempConfiguration.windowConfiguration.getBounds().equals(getBounds())) {
+                mStartingData.mResizedFromTransfer = true;
+                // Lock the starting window to task, so it won't resize from transfer anymore.
+                mActivityRecord.associateStartingWindowWithTaskIfNeeded();
+            }
         }
     }
 
@@ -3907,7 +3916,7 @@
      * LetterboxUiController#shouldShowLetterboxUi} for more context.
      */
     boolean areAppWindowBoundsLetterboxed() {
-        return mActivityRecord != null
+        return mActivityRecord != null && !isStartingWindowAssociatedToTask()
                 && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout());
     }
 
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 17474fb..6a349e2 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -26,6 +26,7 @@
 import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
 import com.android.server.permission.access.immutable.IndexedMap
 import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.DevicePermissionPolicy
 import com.android.server.permission.access.util.attributeInt
 import com.android.server.permission.access.util.attributeInterned
 import com.android.server.permission.access.util.forEachTag
@@ -46,6 +47,7 @@
                 getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
             }
             addPolicy(AppIdPermissionPolicy())
+            addPolicy(DevicePermissionPolicy())
             addPolicy(AppIdAppOpPolicy())
             addPolicy(PackageAppOpPolicy())
         } as IndexedMap<String, IndexedMap<String, SchemePolicy>>
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 4ec32ea..94c878a 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -329,6 +329,18 @@
 private typealias AppIdPermissionFlagsReference =
     MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
 
+
+typealias DevicePermissionFlags =
+    IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias MutableDevicePermissionFlags =
+    MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias AppIdDevicePermissionFlags =
+    IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+typealias MutableAppIdDevicePermissionFlags =
+    MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+private typealias AppIdDevicePermissionFlagsReference =
+    MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
+
 typealias AppIdAppOpModes =
     IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
 typealias MutableAppIdAppOpModes =
@@ -346,6 +358,7 @@
 sealed class UserState(
     internal val packageVersionsReference: PackageVersionsReference,
     internal val appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+    internal val appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
     internal val appIdAppOpModesReference: AppIdAppOpModesReference,
     internal val packageAppOpModesReference: PackageAppOpModesReference,
     defaultPermissionGrantFingerprint: String?,
@@ -357,6 +370,9 @@
     val appIdPermissionFlags: AppIdPermissionFlags
         get() = appIdPermissionFlagsReference.get()
 
+    val appIdDevicePermissionFlags: AppIdDevicePermissionFlags
+        get() = appIdDevicePermissionFlagsReference.get()
+
     val appIdAppOpModes: AppIdAppOpModes
         get() = appIdAppOpModesReference.get()
 
@@ -375,6 +391,7 @@
 class MutableUserState private constructor(
     packageVersionsReference: PackageVersionsReference,
     appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
+    appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
     appIdAppOpModesReference: AppIdAppOpModesReference,
     packageAppOpModesReference: PackageAppOpModesReference,
     defaultPermissionGrantFingerprint: String?,
@@ -382,6 +399,7 @@
 ) : UserState(
     packageVersionsReference,
     appIdPermissionFlagsReference,
+    appIdDevicePermissionFlagsReference,
     appIdAppOpModesReference,
     packageAppOpModesReference,
     defaultPermissionGrantFingerprint,
@@ -390,6 +408,7 @@
     constructor() : this(
         PackageVersionsReference(MutableIndexedMap<String, Int>()),
         AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
+        AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
         AppIdAppOpModesReference(MutableAppIdAppOpModes()),
         PackageAppOpModesReference(MutablePackageAppOpModes()),
         null,
@@ -399,6 +418,7 @@
     internal constructor(userState: UserState) : this(
         userState.packageVersionsReference.toImmutable(),
         userState.appIdPermissionFlagsReference.toImmutable(),
+        userState.appIdDevicePermissionFlagsReference.toImmutable(),
         userState.appIdAppOpModesReference.toImmutable(),
         userState.packageAppOpModesReference.toImmutable(),
         userState.defaultPermissionGrantFingerprint,
@@ -410,6 +430,9 @@
     fun mutateAppIdPermissionFlags(): MutableAppIdPermissionFlags =
         appIdPermissionFlagsReference.mutate()
 
+    fun mutateAppIdDevicePermissionFlags(): MutableAppIdDevicePermissionFlags =
+        appIdDevicePermissionFlagsReference.mutate()
+
     fun mutateAppIdAppOpModes(): MutableAppIdAppOpModes = appIdAppOpModesReference.mutate()
 
     fun mutatePackageAppOpModes(): MutablePackageAppOpModes = packageAppOpModesReference.mutate()
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index d1abc04..1d46ca7 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -65,6 +65,17 @@
     }
 }
 
+data class DevicePermissionUri(
+    val permissionName: String,
+    val deviceId: Int
+) : AccessUri(SCHEME) {
+    override fun toString(): String = "$scheme:///$permissionName/$deviceId"
+
+    companion object {
+        const val SCHEME = "device-permission"
+    }
+}
+
 data class UidUri(
     val uid: Int
 ) : AccessUri(SCHEME) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
new file mode 100644
index 0000000..37a4a90
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionFlags
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutableAppIdDevicePermissionFlags
+import com.android.server.permission.access.MutableDevicePermissionFlags
+import com.android.server.permission.access.WriteMode
+import com.android.server.permission.access.immutable.IndexedMap
+import com.android.server.permission.access.immutable.MutableIndexedMap
+import com.android.server.permission.access.immutable.forEachIndexed
+import com.android.server.permission.access.immutable.forEachReversedIndexed
+import com.android.server.permission.access.immutable.set
+import com.android.server.permission.access.util.andInv
+import com.android.server.permission.access.util.attributeInt
+import com.android.server.permission.access.util.attributeInterned
+import com.android.server.permission.access.util.forEachTag
+import com.android.server.permission.access.util.getAttributeIntOrThrow
+import com.android.server.permission.access.util.getAttributeValueOrThrow
+import com.android.server.permission.access.util.hasBits
+import com.android.server.permission.access.util.tag
+import com.android.server.permission.access.util.tagName
+
+class DevicePermissionPersistence {
+    fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+        when (tagName) {
+            TAG_APP_ID_DEVICE_PERMISSIONS -> parseAppIdDevicePermissions(state, userId)
+            else -> {}
+        }
+    }
+
+    private fun BinaryXmlPullParser.parseAppIdDevicePermissions(
+        state: MutableAccessState,
+        userId: Int
+    ) {
+        val userState = state.mutateUserState(userId, WriteMode.NONE)!!
+        val appIdDevicePermissionFlags = userState.mutateAppIdDevicePermissionFlags()
+        forEachTag {
+            when (tagName) {
+                TAG_APP_ID -> parseAppId(appIdDevicePermissionFlags)
+                else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+            }
+        }
+
+        appIdDevicePermissionFlags.forEachReversedIndexed { appIdIndex, appId, _ ->
+            if (appId !in state.externalState.appIdPackageNames) {
+                Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
+                appIdDevicePermissionFlags.removeAt(appIdIndex)
+                userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
+            }
+        }
+    }
+
+    private fun BinaryXmlPullParser.parseAppId(
+        appIdPermissionFlags: MutableAppIdDevicePermissionFlags
+    ) {
+        val appId = getAttributeIntOrThrow(ATTR_ID)
+        val devicePermissionFlags = MutableDevicePermissionFlags()
+        appIdPermissionFlags[appId] = devicePermissionFlags
+        forEachTag {
+            when (tagName) {
+                TAG_DEVICE -> parseDevice(devicePermissionFlags)
+                else -> {
+                    Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+                }
+            }
+        }
+    }
+
+    private fun BinaryXmlPullParser.parseDevice(
+        deviceIdPermissionFlags: MutableDevicePermissionFlags
+    ) {
+        val deviceId = getAttributeValueOrThrow(ATTR_ID)
+        val permissionFlags = MutableIndexedMap<String, Int>()
+        deviceIdPermissionFlags.put(deviceId, permissionFlags)
+        forEachTag {
+            when (tagName) {
+                TAG_PERMISSION -> parsePermission(permissionFlags)
+                else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
+            }
+        }
+    }
+
+    private fun BinaryXmlPullParser.parsePermission(
+        permissionFlags: MutableIndexedMap<String, Int>
+    ) {
+        val name = getAttributeValueOrThrow(ATTR_NAME).intern()
+        val flags = getAttributeIntOrThrow(ATTR_FLAGS)
+        permissionFlags[name] = flags
+    }
+
+    fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+        val appIdDevicePermissionFlags = state.userStates[userId]!!.appIdDevicePermissionFlags
+        tag(TAG_APP_ID_DEVICE_PERMISSIONS) {
+            appIdDevicePermissionFlags.forEachIndexed { _, appId, devicePermissionFlags ->
+                serializeAppId(appId, devicePermissionFlags)
+            }
+        }
+    }
+
+    private fun BinaryXmlSerializer.serializeAppId(
+        appId: Int,
+        devicePermissionFlags: DevicePermissionFlags
+    ) {
+        tag(TAG_APP_ID) {
+            attributeInt(ATTR_ID, appId)
+            devicePermissionFlags.forEachIndexed { _, deviceId, permissionFlags ->
+                serializeDevice(deviceId, permissionFlags)
+            }
+        }
+    }
+
+    private fun BinaryXmlSerializer.serializeDevice(
+        deviceId: String,
+        permissionFlags: IndexedMap<String, Int>
+    ) {
+        tag(TAG_DEVICE) {
+            attributeInterned(ATTR_ID, deviceId)
+            permissionFlags.forEachIndexed { _, name, flags ->
+                serializePermission(name, flags)
+            }
+        }
+    }
+
+    private fun BinaryXmlSerializer.serializePermission(name: String, flags: Int) {
+        tag(TAG_PERMISSION) {
+            attributeInterned(ATTR_NAME, name)
+            // Never serialize one-time permissions as granted.
+            val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+                flags andInv PermissionFlags.RUNTIME_GRANTED
+            } else {
+                flags
+            }
+            attributeInt(ATTR_FLAGS, serializedFlags)
+        }
+    }
+
+    companion object {
+        private val LOG_TAG = DevicePermissionPersistence::class.java.simpleName
+
+        private const val TAG_APP_ID_DEVICE_PERMISSIONS = "app-id-device-permissions"
+        private const val TAG_APP_ID = "app-id"
+        private const val TAG_DEVICE = "device"
+        private const val TAG_PERMISSION = "permission"
+
+        private const val ATTR_ID = "id"
+        private const val ATTR_NAME = "name"
+        private const val ATTR_FLAGS = "flags"
+    }
+}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
new file mode 100644
index 0000000..c0d7546
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.permission
+
+import android.util.Slog
+import com.android.modules.utils.BinaryXmlPullParser
+import com.android.modules.utils.BinaryXmlSerializer
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.DevicePermissionUri
+import com.android.server.permission.access.GetStateScope
+import com.android.server.permission.access.MutableAccessState
+import com.android.server.permission.access.MutateStateScope
+import com.android.server.permission.access.SchemePolicy
+import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
+import com.android.server.permission.access.util.andInv
+import com.android.server.pm.pkg.PackageState
+
+class DevicePermissionPolicy : SchemePolicy() {
+    private val persistence = DevicePermissionPersistence()
+
+    @Volatile
+    private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
+        MutableIndexedListSet()
+    private val listenersLock = Any()
+
+    override val subjectScheme: String
+        get() = UidUri.SCHEME
+
+    override val objectScheme: String
+        get() = DevicePermissionUri.SCHEME
+
+    override fun GetStateScope.onStateMutated() {
+        listeners.forEachIndexed { _, it -> it.onStateMutated() }
+    }
+
+    override fun MutateStateScope.onAppIdRemoved(appId: Int) {
+        newState.userStates.forEachIndexed { userStateIndex, _, userState ->
+            if (appId in userState.appIdDevicePermissionFlags) {
+                newState.mutateUserStateAt(userStateIndex)
+                    .mutateAppIdDevicePermissionFlags() -= appId
+            }
+        }
+    }
+
+    override fun MutateStateScope.onStorageVolumeMounted(
+        volumeUuid: String?,
+        packageNames: List<String>,
+        isSystemUpdated: Boolean
+    ) {
+        packageNames.forEachIndexed { _, packageName ->
+            val packageState = newState.externalState.packageStates[packageName]!!
+            trimPermissionStates(packageState.appId)
+        }
+    }
+
+    override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
+        trimPermissionStates(packageState.appId)
+    }
+
+    override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
+        if (appId in newState.externalState.appIdPackageNames) {
+            trimPermissionStates(appId)
+        }
+    }
+
+    override fun MutateStateScope.onPackageUninstalled(
+        packageName: String,
+        appId: Int,
+        userId: Int
+    ) {
+        resetPermissionStates(packageName, userId)
+    }
+
+    private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
+        // It's okay to skip resetting permissions for packages that are removed,
+        // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
+        val packageState = newState.externalState.packageStates[packageName] ?: return
+        val androidPackage = packageState.androidPackage ?: return
+        val appId = packageState.appId
+        val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
+        androidPackage.requestedPermissions.forEach { permissionName ->
+            val isRequestedByOtherPackages = anyPackageInAppId(appId) {
+                it.packageName != packageName &&
+                    permissionName in it.androidPackage!!.requestedPermissions
+            }
+            if (isRequestedByOtherPackages) {
+                return@forEach
+            }
+            appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
+                setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+            }
+        }
+    }
+
+    private fun MutateStateScope.trimPermissionStates(appId: Int) {
+        val requestedPermissions = MutableIndexedSet<String>()
+        forEachPackageInAppId(appId) {
+            requestedPermissions += it.androidPackage!!.requestedPermissions
+        }
+        newState.userStates.forEachIndexed { _, userId, userState ->
+            userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
+                    _, deviceId, permissionFlags ->
+                permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
+                    if (permissionName !in requestedPermissions) {
+                        setPermissionFlags(appId, deviceId, userId, permissionName, 0)
+                    }
+                }
+            }
+        }
+    }
+
+    private inline fun MutateStateScope.anyPackageInAppId(
+        appId: Int,
+        state: AccessState = newState,
+        predicate: (PackageState) -> Boolean
+    ): Boolean {
+        val packageNames = state.externalState.appIdPackageNames[appId]!!
+        return packageNames.anyIndexed { _, packageName ->
+            val packageState = state.externalState.packageStates[packageName]!!
+            packageState.androidPackage != null && predicate(packageState)
+        }
+    }
+
+    private inline fun MutateStateScope.forEachPackageInAppId(
+        appId: Int,
+        state: AccessState = newState,
+        action: (PackageState) -> Unit
+    ) {
+        val packageNames = state.externalState.appIdPackageNames[appId]!!
+        packageNames.forEachIndexed { _, packageName ->
+            val packageState = state.externalState.packageStates[packageName]!!
+            if (packageState.androidPackage != null) {
+                action(packageState)
+            }
+        }
+    }
+
+    override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
+        with(persistence) { this@parseUserState.parseUserState(state, userId) }
+    }
+
+    override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
+        with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
+    }
+
+    fun GetStateScope.getPermissionFlags(
+        appId: Int,
+        deviceId: String,
+        userId: Int,
+        permissionName: String
+    ): Int =
+        state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
+            ?.getWithDefault(permissionName, 0) ?: 0
+
+    fun MutateStateScope.setPermissionFlags(
+        appId: Int,
+        deviceId: String,
+        userId: Int,
+        permissionName: String,
+        flags: Int
+    ): Boolean =
+        updatePermissionFlags(
+            appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
+        )
+
+    private fun MutateStateScope.updatePermissionFlags(
+        appId: Int,
+        deviceId: String,
+        userId: Int,
+        permissionName: String,
+        flagMask: Int,
+        flagValues: Int
+    ): Boolean {
+        if (!isDeviceAwarePermission(permissionName)) {
+            Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
+            return false
+        }
+        val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
+            ?.get(deviceId).getWithDefault(permissionName, 0)
+        val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
+        if (oldFlags == newFlags) {
+            return false
+        }
+        val appIdDevicePermissionFlags =
+            newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
+        val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
+            MutableIndexedReferenceMap()
+        }
+        val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
+        permissionFlags.putWithDefault(permissionName, newFlags, 0)
+        if (permissionFlags.isEmpty()) {
+            devicePermissionFlags -= deviceId
+            if (devicePermissionFlags.isEmpty()) {
+                appIdDevicePermissionFlags -= appId
+            }
+        }
+        listeners.forEachIndexed { _, it ->
+            it.onDevicePermissionFlagsChanged(
+                appId, userId, deviceId, permissionName, oldFlags, newFlags
+            )
+        }
+        return true
+    }
+
+    fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+        synchronized(listenersLock) {
+            listeners = listeners + listener
+        }
+    }
+
+    fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
+        synchronized(listenersLock) {
+            listeners = listeners - listener
+        }
+    }
+
+    private fun isDeviceAwarePermission(permissionName: String): Boolean =
+        DEVICE_SUPPORTED_PERMISSIONS.contains(permissionName)
+
+    companion object {
+        private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
+
+        /**
+         * These permissions are supported for virtual devices.
+         */
+        private val DEVICE_SUPPORTED_PERMISSIONS = indexedSetOf(
+            android.Manifest.permission.CAMERA,
+            android.Manifest.permission.RECORD_AUDIO
+        )
+    }
+
+    /**
+     * TODO: b/289355341 - implement listener for permission changes
+     * Listener for permission flags changes.
+     */
+    abstract class OnDevicePermissionFlagsChangedListener {
+        /**
+         * Called when a permission flags change has been made to the upcoming new state.
+         *
+         * Implementations should keep this method fast to avoid stalling the locked state mutation,
+         * and only call external code after [onStateMutated] when the new state has actually become
+         * the current state visible to external code.
+         */
+        abstract fun onDevicePermissionFlagsChanged(
+            appId: Int,
+            userId: Int,
+            deviceId: String,
+            permissionName: String,
+            oldFlags: Int,
+            newFlags: Int
+        )
+
+        /**
+         * Called when the upcoming new state has become the current state.
+         *
+         * Implementations should keep this method fast to avoid stalling the locked state mutation.
+         */
+        abstract fun onStateMutated()
+    }
+}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index edacf188..d9f179a 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -64,9 +64,11 @@
 import com.android.server.PermissionThread
 import com.android.server.ServiceThread
 import com.android.server.SystemConfig
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal
 import com.android.server.permission.access.AccessCheckingService
 import com.android.server.permission.access.AccessState
 import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.DevicePermissionUri
 import com.android.server.permission.access.GetStateScope
 import com.android.server.permission.access.MutateStateScope
 import com.android.server.permission.access.PermissionUri
@@ -110,6 +112,9 @@
     private val policy =
         service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
 
+    private val devicePolicy =
+        service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
+
     private val context = service.context
     private lateinit var metricsLogger: MetricsLogger
     private lateinit var packageManagerInternal: PackageManagerInternal
@@ -132,6 +137,8 @@
 
     private lateinit var permissionControllerManager: PermissionControllerManager
 
+    private lateinit var virtualDeviceManagerInternal: VirtualDeviceManagerInternal
+
     /**
      * A permission backup might contain apps that are not installed. In this case we delay the
      * restoration until the app is installed.
@@ -152,6 +159,8 @@
         systemConfig = SystemConfig.getInstance()
         userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
         userManagerService = UserManagerService.getInstance()
+        virtualDeviceManagerInternal =
+            LocalServices.getService(VirtualDeviceManagerInternal::class.java)
 
         // The package info cache is the cache for package and permission information.
         // Disable the package info and package permission caches locally but leave the
@@ -460,7 +469,7 @@
         return size
     }
 
-    override fun checkUidPermission(uid: Int, permissionName: String): Int {
+    override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
         val userId = UserHandle.getUserId(uid)
         if (!userManagerInternal.exists(userId)) {
             return PackageManager.PERMISSION_DENIED
@@ -482,7 +491,7 @@
                 return PackageManager.PERMISSION_DENIED
             }
             val isPermissionGranted = service.getState {
-                isPermissionGranted(packageState, userId, permissionName)
+                isPermissionGranted(packageState, userId, permissionName, deviceId)
             }
             return if (isPermissionGranted) {
                 PackageManager.PERMISSION_GRANTED
@@ -515,7 +524,12 @@
         return false
     }
 
-    override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
+    override fun checkPermission(
+        packageName: String,
+        permissionName: String,
+        deviceId: Int,
+        userId: Int
+    ): Int {
         if (!userManagerInternal.exists(userId)) {
             return PackageManager.PERMISSION_DENIED
         }
@@ -524,7 +538,7 @@
             .use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
 
         val isPermissionGranted = service.getState {
-            isPermissionGranted(packageState, userId, permissionName)
+            isPermissionGranted(packageState, userId, permissionName, deviceId)
         }
         return if (isPermissionGranted) {
             PackageManager.PERMISSION_GRANTED
@@ -542,19 +556,21 @@
     private fun GetStateScope.isPermissionGranted(
         packageState: PackageState,
         userId: Int,
-        permissionName: String
+        permissionName: String,
+        deviceId: Int
     ): Boolean {
         val appId = packageState.appId
         // Note that instant apps can't have shared UIDs, so we only need to check the current
         // package state.
         val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
-        if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
+        if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
             return true
         }
 
         val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
         if (fullerPermissionName != null &&
-            isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
+            isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
+        ) {
             return true
         }
 
@@ -568,9 +584,10 @@
         appId: Int,
         userId: Int,
         isInstantApp: Boolean,
-        permissionName: String
+        permissionName: String,
+        deviceId: Int,
     ): Boolean {
-        val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+        val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
         if (!PermissionFlags.isPermissionGranted(flags)) {
             return false
         }
@@ -601,7 +618,8 @@
                 ?: return emptySet()
 
             return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
-                if (isPermissionGranted(packageState, userId, permissionName)) {
+                if (isPermissionGranted(
+                        packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
                     permissionName
                 } else {
                     null
@@ -640,18 +658,26 @@
         }
     }
 
-    override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
-        setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
+    override fun grantRuntimePermission(
+        packageName: String,
+        permissionName: String,
+        deviceId: Int,
+        userId: Int
+    ) {
+        setRuntimePermissionGranted(
+            packageName, userId, permissionName, deviceId, isGranted = true
+        )
     }
 
     override fun revokeRuntimePermission(
         packageName: String,
         permissionName: String,
+        deviceId: Int,
         userId: Int,
         reason: String?
     ) {
         setRuntimePermissionGranted(
-            packageName, userId, permissionName, isGranted = false, revokeReason = reason
+            packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
         )
     }
 
@@ -660,8 +686,8 @@
         userId: Int
     ) {
         setRuntimePermissionGranted(
-            packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
-            skipKillUid = true
+            packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
+            isGranted = false, skipKillUid = true
         )
     }
 
@@ -673,6 +699,7 @@
         packageName: String,
         userId: Int,
         permissionName: String,
+        deviceId: Int,
         isGranted: Boolean,
         skipKillUid: Boolean = false,
         revokeReason: String? = null
@@ -748,7 +775,7 @@
             }
 
             setRuntimePermissionGranted(
-                packageState, userId, permissionName, isGranted, canManageRolePermission,
+                packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
                 overridePolicyFixed, reportError = true, methodName
             )
         }
@@ -782,14 +809,16 @@
                         if (permissionState ==
                             PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
                             setRuntimePermissionGranted(
-                                packageState, userId, permissionName, isGranted = true,
-                                canManageRolePermission = false, overridePolicyFixed = false,
-                                reportError = false, "setRequestedPermissionStates"
+                                packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+                                isGranted = true, canManageRolePermission = false,
+                                overridePolicyFixed = false, reportError = false,
+                                "setRequestedPermissionStates"
                             )
                             updatePermissionFlags(
                                 packageState.appId, userId, permissionName,
+                                Context.DEVICE_ID_DEFAULT,
                                 PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
-                                    PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+                                PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
                                 canUpdateSystemFlags = false,
                                 reportErrorForUnknownPermission = false,
                                 isPermissionRequested = true, "setRequestedPermissionStates",
@@ -816,6 +845,7 @@
         packageState: PackageState,
         userId: Int,
         permissionName: String,
+        deviceId: Int,
         isGranted: Boolean,
         canManageRolePermission: Boolean,
         overridePolicyFixed: Boolean,
@@ -871,7 +901,7 @@
         }
 
         val appId = packageState.appId
-        val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+        val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
 
         if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
             if (reportError) {
@@ -934,7 +964,7 @@
             return
         }
 
-        with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+        setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
 
         if (permission.isRuntime) {
             val action = if (isGranted) {
@@ -963,7 +993,12 @@
         with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
     }
 
-    override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
+    override fun getPermissionFlags(
+        packageName: String,
+        permissionName: String,
+        deviceId: Int,
+        userId: Int,
+    ): Int {
         if (!userManagerInternal.exists(userId)) {
             Slog.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
             return 0
@@ -994,7 +1029,8 @@
             }
 
             val flags =
-                with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+                getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
             return PermissionFlags.toApiFlags(flags)
         }
     }
@@ -1002,6 +1038,7 @@
     override fun isPermissionRevokedByPolicy(
         packageName: String,
         permissionName: String,
+        deviceId: Int,
         userId: Int
     ): Boolean {
         if (!userManagerInternal.exists(userId)) {
@@ -1018,13 +1055,13 @@
             .use { it.getPackageState(packageName) } ?: return false
 
         service.getState {
-            if (isPermissionGranted(packageState, userId, permissionName)) {
+            if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
                 return false
             }
 
-            val flags = with(policy) {
-                getPermissionFlags(packageState.appId, userId, permissionName)
-            }
+            val flags =
+                getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
+
             return flags.hasBits(PermissionFlags.POLICY_FIXED)
         }
     }
@@ -1046,7 +1083,8 @@
     override fun shouldShowRequestPermissionRationale(
         packageName: String,
         permissionName: String,
-        userId: Int
+        deviceId: Int,
+        userId: Int,
     ): Boolean {
         if (!userManagerInternal.exists(userId)) {
             Slog.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
@@ -1068,11 +1106,11 @@
 
         val flags: Int
         service.getState {
-            if (isPermissionGranted(packageState, userId, permissionName)) {
+            if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
                 return false
             }
 
-            flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+            flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
         }
         if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
             return false
@@ -1104,6 +1142,7 @@
         flagMask: Int,
         flagValues: Int,
         enforceAdjustPolicyPermission: Boolean,
+        deviceId: Int,
         userId: Int
     ) {
         val callingUid = Binder.getCallingUid()
@@ -1199,7 +1238,7 @@
         val appId = packageState.appId
         service.mutateState {
             updatePermissionFlags(
-                appId, userId, permissionName, flagMask, flagValues, canUpdateSystemFlags,
+                appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
                 reportErrorForUnknownPermission = true, isPermissionRequested,
                 "updatePermissionFlags", packageName
             )
@@ -1248,8 +1287,9 @@
                 val androidPackage = packageState.androidPackage ?: return@forEach
                 androidPackage.requestedPermissions.forEach { permissionName ->
                     updatePermissionFlags(
-                        packageState.appId, userId, permissionName, flagMask, flagValues,
-                        canUpdateSystemFlags, reportErrorForUnknownPermission = false,
+                        packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
+                        flagMask, flagValues, canUpdateSystemFlags,
+                        reportErrorForUnknownPermission = false,
                         isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
                     )
                 }
@@ -1264,6 +1304,7 @@
         appId: Int,
         userId: Int,
         permissionName: String,
+        deviceId: Int,
         flagMask: Int,
         flagValues: Int,
         canUpdateSystemFlags: Boolean,
@@ -1298,7 +1339,7 @@
             return
         }
 
-        val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
+        val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
         if (!isPermissionRequested && oldFlags == 0) {
             Slog.w(
                 LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
@@ -1308,7 +1349,7 @@
         }
 
         val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
-        with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
+        setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
     }
 
     override fun getAllowlistedRestrictedPermissions(
@@ -1365,6 +1406,49 @@
         )
     }
 
+    private fun GetStateScope.getPermissionFlagsWithPolicy(
+        appId: Int,
+        userId: Int,
+        permissionName: String,
+        deviceId: Int,
+    ): Int =
+        if (deviceId == Context.DEVICE_ID_DEFAULT) {
+            with(policy) { getPermissionFlags(appId, userId, permissionName) }
+        } else {
+            val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+            if (persistentDeviceId != null) {
+                with(devicePolicy) {
+                    getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
+                }
+            } else {
+                Slog.e(LOG_TAG, "Invalid deviceId $deviceId, no persistent device ID found.")
+                0
+            }
+        }
+
+    private fun MutateStateScope.setPermissionFlagsWithPolicy(
+        appId: Int,
+        userId: Int,
+        permissionName: String,
+        deviceId: Int,
+        flags: Int
+    ): Boolean =
+        if (deviceId == Context.DEVICE_ID_DEFAULT) {
+            with(policy) {
+                setPermissionFlags(appId, userId, permissionName, flags)
+            }
+        } else {
+            val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+            if (persistentDeviceId != null) {
+                with(devicePolicy) {
+                    setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
+                }
+            } else {
+                Slog.e(LOG_TAG, "Invalid deviceId $deviceId, no cdm association found.")
+                false
+            }
+        }
+
     /**
      * This method does not enforce checks on the caller, should only be called after
      * required checks.
@@ -1539,8 +1623,7 @@
     ) {
         service.mutateState {
             with(policy) {
-                val permissionsFlags =
-                    getUidPermissionFlags(appId, userId) ?: return@mutateState
+                val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
 
                 val permissions = getPermissions()
                 androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
@@ -1661,8 +1744,6 @@
         )
     }
 
-
-
     override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
         requireNotNull(permissionName) { "permissionName cannot be null" }
         val packageNames = ArraySet<String>()
@@ -1879,7 +1960,7 @@
                     println("Permissions:")
                     withIndent {
                         userState.appIdPermissionFlags[appId]?.forEachIndexed {
-                                _, permissionName, flags ->
+                            _, permissionName, flags ->
                             val isGranted = PermissionFlags.isPermissionGranted(flags)
                             println(
                                 "$permissionName: granted=$isGranted, flags=" +
@@ -1888,6 +1969,20 @@
                         }
                     }
 
+                    userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
+                            _, deviceId, devicePermissionFlags ->
+                        println("Permissions (Device $deviceId):")
+                        withIndent {
+                            devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
+                                val isGranted = PermissionFlags.isPermissionGranted(flags)
+                                println(
+                                    "$permissionName: granted=$isGranted, flags=" +
+                                        PermissionFlags.toString(flags)
+                                )
+                            }
+                        }
+                    }
+
                     println("App ops:")
                     withIndent {
                         userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
@@ -2412,7 +2507,7 @@
         }
 
         private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
-            if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
+            if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
                 PackageManager.PERMISSION_GRANTED) {
                 return false
             }
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 35b9bc3..4a8d73d2 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -254,12 +254,45 @@
             }
             final long identity = Binder.clearCallingIdentity();
             try {
-                return userState.getCustomPrinterIcon(printerId);
+                Icon icon = userState.getCustomPrinterIcon(printerId);
+                return validateIconUserBoundary(icon);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
+        /**
+         * Validates the custom printer icon to see if it's not in the calling user space.
+         * If the condition is not met, return null. Otherwise, return the original icon.
+         *
+         * @param icon
+         * @return icon (validated)
+         */
+        private Icon validateIconUserBoundary(Icon icon) {
+            // Refer to Icon#getUriString for context. The URI string is invalid for icons of
+            // incompatible types.
+            if (icon != null && (icon.getType() == Icon.TYPE_URI
+                    || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
+                String encodedUser = icon.getUri().getEncodedUserInfo();
+
+                // If there is no encoded user, the URI is calling into the calling user space
+                if (encodedUser != null) {
+                    int userId = Integer.parseInt(encodedUser);
+                    // resolve encoded user
+                    final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+
+                    synchronized (mLock) {
+                        // Only the current group members can get the printer icons.
+                        if (resolveCallingProfileParentLocked(resolvedUserId)
+                                != getCurrentUserId()) {
+                            return null;
+                        }
+                    }
+                }
+            }
+            return icon;
+        }
+
         @Override
         public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
             if (printJobId == null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index cd3a78e..6906dec 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -2157,6 +2157,14 @@
     }
 
     @Test
+    public void testResetGamePowerMode() {
+        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        gameManagerService.onBootCompleted();
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, false);
+        verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
+    }
+
+    @Test
     public void testNotifyGraphicsEnvironmentSetup() {
         String configString = "mode=2,loadingBoost=2000";
         when(DeviceConfig.getProperty(anyString(), anyString()))
diff --git a/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log b/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log
new file mode 100644
index 0000000..ee331c2
--- /dev/null
+++ b/services/tests/servicestests/res/raw-watch/a11y_three_finger_swipe_down_gesture.log
@@ -0,0 +1,29 @@
+ * Gesture6_id30:Swipe down with 3 finger
+MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=5273700, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_DOWN(1), actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273700, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273709, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_DOWN(2), actionButton=0, id[0]=0, x[0]=150.0, y[0]=696.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=214.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=100.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273709, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=50.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=70.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=100.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273715, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=60.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=80.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=110.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273725, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=70.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=90.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=120.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273734, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=80.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=100.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=130.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273741, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=90.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=110.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=140.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273750, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=100.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=120.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=150.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273758, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=110.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=130.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=160.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273767, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=120.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=140.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=170.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273776, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=130.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=150.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=180.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273784, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=140.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=160.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=190.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273793, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=150.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=170.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=200.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273801, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=160.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=180.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=210.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273810, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=170.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=190.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=220.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273822, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=180.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=200.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=230.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273831, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=190.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=210.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=240.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273836, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=200.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=220.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=250.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273844, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=210.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=230.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=260.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273853, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=220.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=240.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=270.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273862, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=230.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=250.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=280.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273870, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=240.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=260.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=290.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273879, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=150.0, y[0]=250.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=270.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=300.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273888, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_UP(0), actionButton=0, id[0]=0, x[0]=150.0, y[0]=250.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=1, x[1]=216.0, y[1]=270.0, toolType[1]=TOOL_TYPE_FINGER, id[2]=2, x[2]=94.0, y[2]=300.0, toolType[2]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=3, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_POINTER_UP(0), actionButton=0, id[0]=1, x[0]=216.0, y[0]=270.0, toolType[0]=TOOL_TYPE_FINGER, id[1]=2, x[1]=94.0, y[1]=300.0, toolType[1]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=2, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
+MotionEvent { action=ACTION_UP, actionButton=0, id[0]=2, x[0]=94.0, y[0]=300.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=5273895, downTime=5273700, deviceId=4, source=0x1002, displayId=0 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index cc1100b..e5909a4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -36,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -297,13 +298,15 @@
                 TestData.getInsecureCertPathForEndpoint1());
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
 
+        setExpectedScryptArgument(password.getBytes());
+
         mKeySyncTask.run();
 
         KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
                 isEqualTo(UI_FORMAT_PASSWORD);
-        verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+        verify(mMockScrypt).scrypt(any(), any(),
                 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
                 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
         KeyDerivationParams keyDerivationParams =
@@ -314,6 +317,44 @@
     }
 
     @Test
+    public void run_zeroizedCredential() throws Exception {
+        String password = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "123";
+        String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias";
+        byte[] zeroizedCredential = password.getBytes();
+        mKeySyncTask = new KeySyncTask(
+                mRecoverableKeyStoreDb,
+                mRecoverySnapshotStorage,
+                mSnapshotListenersStorage,
+                TEST_USER_ID,
+                CREDENTIAL_TYPE_PASSWORD,
+                /*credential=*/ zeroizedCredential,
+                /*credentialUpdated=*/ false,
+                mPlatformKeyManager,
+                mTestOnlyInsecureCertificateHelper,
+                mMockScrypt);
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+                TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+                TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
+                TestData.getInsecureCertPathForEndpoint1());
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
+
+        // Need to check array value during method call since it is modified later.
+        setExpectedScryptArgument(password.getBytes());
+
+        Arrays.fill(zeroizedCredential, (byte) 0);
+        mKeySyncTask.run();
+
+        verify(mMockScrypt).scrypt(any(), any(),
+                eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+                eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+    }
+
+    @Test
     public void run_useSha256ToHashPatternInProdMode() throws Exception {
         String pattern = "123456";
         mKeySyncTask = new KeySyncTask(
@@ -368,13 +409,15 @@
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
 
+        setExpectedScryptArgument(shortPassword.getBytes());
+
         mKeySyncTask.run();
 
         KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
                 isEqualTo(UI_FORMAT_PASSWORD);
-        verify(mMockScrypt).scrypt(eq(shortPassword.getBytes()), any(),
+        verify(mMockScrypt).scrypt(any(), any(),
                 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
                 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
         KeyDerivationParams keyDerivationParams =
@@ -650,13 +693,15 @@
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
+        setExpectedScryptArgument(password.getBytes());
+
         mKeySyncTask.run();
 
         KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
         assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
                 isEqualTo(UI_FORMAT_PASSWORD);
-        verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+        verify(mMockScrypt).scrypt(any(), any(),
                 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
                 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
     }
@@ -681,6 +726,8 @@
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
+        setExpectedScryptArgument(pin.getBytes());
+
         mKeySyncTask.run();
 
         KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
@@ -688,7 +735,7 @@
         // Password with only digits is changed to pin.
         assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
                 isEqualTo(UI_FORMAT_PIN);
-        verify(mMockScrypt).scrypt(eq(pin.getBytes()), any(),
+        verify(mMockScrypt).scrypt(any(), any(),
                 eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
                 eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
     }
@@ -868,4 +915,14 @@
         new Random().nextBytes(bytes);
         return bytes;
     }
+
+    private void setExpectedScryptArgument(byte[] credentials) {
+        doAnswer(invocation -> {
+            assertThat((byte[]) invocation.getArguments()[0]).isEqualTo(credentials);
+            return invocation.callRealMethod();
+        }).when(mMockScrypt).scrypt(any(), any(),
+                eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+                eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index 8c07b6c..9f855c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -52,7 +52,6 @@
             factory.isNamespaceAware = true
             factory.newPullParser()
         }
-        private val ns = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
     }
 
     @Test
@@ -102,9 +101,13 @@
         val tag = "application"
         validateTagAttr(tag, "backupAgent",
             R.styleable.AndroidManifestApplication_backupAgent, 1024)
+        validateTagAttrComponentName(tag, "backupAgent",
+            R.styleable.AndroidManifestApplication_backupAgent)
         validateTagAttr(tag, "manageSpaceActivity",
             R.styleable.AndroidManifestApplication_manageSpaceActivity, 1024)
         validateTagAttr(tag, "name", R.styleable.AndroidManifestApplication_name, 1024)
+        validateTagAttrComponentName(tag, "name",
+            R.styleable.AndroidManifestApplication_name)
         validateTagAttr(tag, "permission", R.styleable.AndroidManifestApplication_permission, 1024)
         validateTagAttr(tag, "process", R.styleable.AndroidManifestApplication_process, 1024)
         validateTagAttr(tag, "requiredAccountType",
@@ -134,6 +137,7 @@
     fun parseReceiverTag() {
         val tag = "receiver"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestReceiver_name, 1024)
+        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestReceiver_name)
         validateTagAttr(tag, "permission", R.styleable.AndroidManifestReceiver_permission, 1024)
         validateTagAttr(tag, "process", R.styleable.AndroidManifestReceiver_process, 1024)
         validateTagCount("meta-data", 1000, tag)
@@ -144,6 +148,7 @@
     fun parseServiceTag() {
         val tag = "service"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestService_name, 1024)
+        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestService_name)
         validateTagAttr(tag, "permission", R.styleable.AndroidManifestService_permission, 1024)
         validateTagAttr(tag, "process", R.styleable.AndroidManifestService_process, 1024)
         validateTagCount("meta-data", 1000, tag)
@@ -158,6 +163,8 @@
             R.styleable.AndroidManifestActivityAlias_permission, 1024)
         validateTagAttr(tag, "targetActivity",
             R.styleable.AndroidManifestActivityAlias_targetActivity, 1024)
+        validateTagAttrComponentName(tag, "targetActivity",
+            R.styleable.AndroidManifestActivityAlias_targetActivity)
         validateTagCount("meta-data", 1000, tag)
         validateTagCount("intent-filter", 20000, tag)
     }
@@ -166,14 +173,18 @@
     fun parseUsesLibraryTag() {
         val tag = "uses-library"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesLibrary_name, 1024)
+        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestUsesLibrary_name)
     }
 
     @Test
     fun parseActivityTag() {
         val tag = "activity"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestActivity_name, 1024)
+        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestActivity_name)
         validateTagAttr(tag, "parentActivityName",
             R.styleable.AndroidManifestActivity_parentActivityName, 1024)
+        validateTagAttrComponentName(tag, "parentActivityName",
+            R.styleable.AndroidManifestActivity_parentActivityName)
         validateTagAttr(tag, "permission", R.styleable.AndroidManifestActivity_permission, 1024)
         validateTagAttr(tag, "process", R.styleable.AndroidManifestActivity_process, 1024)
         validateTagAttr(tag, "taskAffinity", R.styleable.AndroidManifestActivity_taskAffinity, 1024)
@@ -200,6 +211,8 @@
     fun parseInstrumentationTag() {
         val tag = "instrumentation"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestInstrumentation_name, 1024)
+        validateTagAttrComponentName(tag, "name",
+            R.styleable.AndroidManifestInstrumentation_name)
         validateTagAttr(tag, "targetPackage",
             R.styleable.AndroidManifestInstrumentation_targetPackage, 256)
         validateTagAttr(tag, "targetProcesses",
@@ -262,6 +275,7 @@
     fun parseProviderTag() {
         val tag = "provider"
         validateTagAttr(tag, "name", R.styleable.AndroidManifestProvider_name, 1024)
+        validateTagAttrComponentName(tag, "name", R.styleable.AndroidManifestProvider_name)
         validateTagAttr(tag, "permission", R.styleable.AndroidManifestProvider_permission, 1024)
         validateTagAttr(tag, "process", R.styleable.AndroidManifestProvider_process, 1024)
         validateTagAttr(tag, "readPermission",
@@ -361,6 +375,48 @@
         validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
     }
 
+    private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
+        val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
+        for (name in passNames) {
+            val xml = "<$tag $attr=\"$name\" />"
+            pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
+            val validator = Validator()
+            pullParser.nextTag()
+            validator.validate(pullParser)
+            try {
+                validator.validateStrAttr(pullParser, attr, name)
+            } catch (e: SecurityException) {
+                fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+                        " ${e.message}")
+            }
+            try {
+                validator.validateResStrAttr(pullParser, index, name)
+            } catch (e: SecurityException) {
+                fail("Failed to parse attribute $attr in <$tag> as valid Java class name:" +
+                        " ${e.message}")
+            }
+        }
+
+        val failNames = arrayOf("com.android.TestClass:", "-TestClass", "TestClass.", ".", "..")
+        for (name in failNames) {
+            val xml = "<$tag $attr=\"$name\" />"
+            pullParser.setInput(ByteArrayInputStream(xml.toByteArray()), null)
+            val validator = Validator()
+            pullParser.nextTag()
+            validator.validate(pullParser)
+            val e1 = assertThrows("$name is not valid Java class name",
+                SecurityException::class.java) {
+                validator.validateStrAttr(pullParser, attr, name)
+            }
+            assertEquals(expectedAttrComponentNameErrorMsg(name), e1.message)
+            val e2 = assertThrows("$name is not valid Java class name",
+                SecurityException::class.java) {
+                validator.validateResStrAttr(pullParser, index, name)
+            }
+            assertEquals(expectedAttrComponentNameErrorMsg(name), e2.message)
+        }
+    }
+
     private fun validateTagAttr(tag: String, name: String, index: Int?, maxLen: Int) {
         validateTagAttr_shouldPass(tag, name, index, maxLen)
         validateTagAttr_shouldFail(tag, name, index, maxLen)
@@ -468,4 +524,6 @@
 
     fun expectedResAttrLengthErrorMsg(tag: String) =
             "String length limit exceeded for attribute in $tag"
+
+    fun expectedAttrComponentNameErrorMsg(name: String) = "$name is not a valid Java class name"
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
index 62875e5..896edff 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -27,6 +27,7 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,6 +42,11 @@
 public class CombinationKeyTests extends ShortcutKeyTestBase {
     private static final long A11Y_KEY_HOLD_MILLIS = 3500;
 
+    @Before
+    public void setUp() {
+        setUpPhoneWindowManager();
+    }
+
     /**
      * Power-VolDown to take screenshot.
      */
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 9029bc4..2c35cf0 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -44,6 +44,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.Before;
 import org.junit.Test;
 
 @Presubmit
@@ -61,6 +62,11 @@
         META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING);
     }
 
+    @Before
+    public void setUp() {
+        setUpPhoneWindowManager();
+    }
+
     /**
      * Test meta+ shortcuts defined in bookmarks.xml.
      */
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index c3b7849..6f65406 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.view.Display;
 
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -35,6 +36,11 @@
  *  atest WmTests:PowerKeyGestureTests
  */
 public class PowerKeyGestureTests extends ShortcutKeyTestBase {
+    @Before
+    public void setUp() {
+        setUpPhoneWindowManager();
+    }
+
     /**
      * Power single press to turn screen on/off.
      */
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index bf88ce4..f83aecb 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -39,6 +39,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
 
@@ -53,12 +54,17 @@
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
 import org.junit.After;
-import org.junit.Before;
+import org.junit.Rule;
 
 import java.util.Map;
 
 class ShortcutKeyTestBase {
+    @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
     TestPhoneWindowManager mPhoneWindowManager;
     final Context mContext = spy(getInstrumentation().getTargetContext());
 
@@ -78,18 +84,35 @@
         MODIFIER = unmodifiableMap(map);
     }
 
-    @Before
-    public void setUp() {
+    /** Same as {@link setUpPhoneWindowManager(boolean)}, without supporting settings update. */
+    protected final void setUpPhoneWindowManager() {
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ false);
+    }
+
+    /**
+     * Creates and sets up a {@link TestPhoneWindowManager} instance.
+     *
+     * <p>Subclasses must call this at the start of the test if they intend to interact with phone
+     * window manager.
+     *
+     * @param supportSettingsUpdate {@code true} if this test should read and listen to provider
+     *      settings values.
+     */
+    protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
 
-        mPhoneWindowManager = new TestPhoneWindowManager(mContext);
+        doReturn(mSettingsProviderRule.mockContentResolver(mContext))
+                .when(mContext).getContentResolver();
+        mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
     }
 
     @After
     public void tearDown() {
-        mPhoneWindowManager.tearDown();
+        if (mPhoneWindowManager != null) {
+            mPhoneWindowManager.tearDown();
+        }
     }
 
     void sendKeyCombination(int[] keyCodes, long duration) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index feca326..e474317 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -221,9 +221,8 @@
     }
 
     @Before
-    @Override
     public void setUp() {
-        super.setUp();
+        setUpPhoneWindowManager();
         mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID);
         mPhoneWindowManager.overrideLaunchHome();
         mPhoneWindowManager.overrideSearchKeyBehavior(
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index fe8017e..c433e64 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,21 +16,14 @@
 
 package com.android.server.policy;
 
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
 import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-
-import android.content.Context;
-import android.content.res.Resources;
+import android.provider.Settings;
 
 import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
 
 /**
  * Test class for stem key gesture.
@@ -39,17 +32,13 @@
  * atest WmTests:StemKeyGestureTests
  */
 public class StemKeyGestureTests extends ShortcutKeyTestBase {
-    @Mock private Resources mResources;
-
     /**
      * Stem single key should not launch behavior during set up.
      */
     @Test
     public void stemSingleKey_duringSetup_doNothing() {
-        stemKeySetup(
-                () -> overrideBehavior(
-                        com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
-                        SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS));
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
         mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
         mPhoneWindowManager.overrideIsUserSetupComplete(false);
 
@@ -63,10 +52,9 @@
      */
     @Test
     public void stemSingleKey_AfterSetup_openAllApp() {
-        stemKeySetup(
-                () -> overrideBehavior(
-                        com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
-                        SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS));
+        overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        mPhoneWindowManager.overrideStartActivity();
         mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
         mPhoneWindowManager.overrideIsUserSetupComplete(true);
 
@@ -75,28 +63,7 @@
         mPhoneWindowManager.assertOpenAllAppView();
     }
 
-    private void stemKeySetup(Runnable behaviorOverrideRunnable) {
-        super.tearDown();
-        setupResourcesMock();
-        behaviorOverrideRunnable.run();
-        super.setUp();
-    }
-
-    private void setupResourcesMock() {
-        Resources realResources = mContext.getResources();
-
-        mResources = Mockito.mock(Resources.class);
-        doReturn(mResources).when(mContext).getResources();
-
-        doAnswer(invocation -> realResources.getXml((Integer) invocation.getArguments()[0]))
-                .when(mResources).getXml(anyInt());
-        doAnswer(invocation -> realResources.getString((Integer) invocation.getArguments()[0]))
-                .when(mResources).getString(anyInt());
-        doAnswer(invocation -> realResources.getBoolean((Integer) invocation.getArguments()[0]))
-                .when(mResources).getBoolean(anyInt());
-    }
-
-    private void overrideBehavior(int resId, int expectedBehavior) {
-        doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
+    private void overrideBehavior(String key, int expectedBehavior) {
+        Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 1866767..7e7a9e1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.description;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -177,16 +178,16 @@
         }
     }
 
-    TestPhoneWindowManager(Context context) {
+    TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
         MockitoAnnotations.initMocks(this);
         mHandlerThread = new HandlerThread("fake window manager");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mContext = mockingDetails(context).isSpy() ? context : spy(context);
-        mHandler.runWithScissors(this::setUp,  0 /* timeout */);
+        mHandler.runWithScissors(() -> setUp(supportSettingsUpdate),  0 /* timeout */);
     }
 
-    private void setUp() {
+    private void setUp(boolean supportSettingsUpdate) {
         mPhoneWindowManager = spy(new PhoneWindowManager());
 
         // Use stubOnly() to reduce memory usage if it doesn't need verification.
@@ -266,7 +267,15 @@
         });
 
         doNothing().when(mPhoneWindowManager).initializeHdmiState();
-        doNothing().when(mPhoneWindowManager).updateSettings();
+        if (supportSettingsUpdate) {
+            doAnswer(inv -> {
+                // Make any call to updateSettings run synchronously for tests.
+                mPhoneWindowManager.updateSettings(null);
+                return null;
+            }).when(mPhoneWindowManager).updateSettings(any(Handler.class));
+        } else {
+            doNothing().when(mPhoneWindowManager).updateSettings(any());
+        }
         doNothing().when(mPhoneWindowManager).screenTurningOn(anyInt(), any());
         doNothing().when(mPhoneWindowManager).screenTurnedOn(anyInt());
         doNothing().when(mPhoneWindowManager).startedWakingUp(anyInt(), anyInt());
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 6d7f2c1..1c86758 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -570,6 +570,7 @@
         final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext));
         final Resources resourcesSpy = Mockito.spy(contextSpy.getResources());
 
+        spyOn(mAtm.mTaskOrganizerController);
         when(contextSpy.getResources()).thenReturn(resourcesSpy);
 
         MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
@@ -597,7 +598,8 @@
                         mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
         assertTrue(toHomeBuilder.mIsLaunchBehind);
         toHomeBuilder.build();
-        verify(animationHandler, never()).createStartingSurface(any());
+        verify(mAtm.mTaskOrganizerController, never())
+                .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         animationHandler.clearBackAnimateTarget();
 
         // Back to ACTIVITY and TASK have the same logic, just with different target.
@@ -609,9 +611,11 @@
         assertFalse(toActivityBuilder.mIsLaunchBehind);
         toActivityBuilder.build();
         if (preferWindowlessSurface) {
-            verify(animationHandler).createStartingSurface(any());
+            verify(mAtm.mTaskOrganizerController)
+                    .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         } else {
-            verify(animationHandler, never()).createStartingSurface(any());
+            verify(mAtm.mTaskOrganizerController, never())
+                    .addWindowlessStartingSurface(any(), any(), any(), any(), any());
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index 1180ebd..c3db241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -47,6 +47,7 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.WindowContextInfo;
 import android.window.WindowTokenClient;
 
 import com.android.server.inputmethod.InputMethodDialogWindowContext;
@@ -99,7 +100,7 @@
             final WindowProcessController wpc = mAtm.getProcessController(appThread);
             mWm.mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                     dc.getImeContainer(), TYPE_INPUT_METHOD_DIALOG, null /* options */);
-            return dc.getImeContainer().getConfiguration();
+            return new WindowContextInfo(dc.getImeContainer().getConfiguration(), displayId);
         }).when(mIWindowManager).attachWindowContextToDisplayArea(any(), any(),
                 eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
         mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 325176f..55fda05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -468,7 +468,7 @@
         mWm.attachWindowContextToWindowToken(mAppThread, new Binder(), windowToken.token);
 
         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(
-                any(), any(), any(), anyInt(), any());
+                any(), any(), any(), anyInt(), any(), anyBoolean());
     }
 
     @Test
@@ -484,9 +484,9 @@
         final IBinder clientToken = new Binder();
         mWm.attachWindowContextToWindowToken(mAppThread, clientToken, windowToken.token);
         final WindowProcessController wpc = mAtm.getProcessController(mAppThread);
-        verify(mWm.mWindowContextListenerController).registerWindowContainerListener(eq(wpc),
-                eq(clientToken), eq(windowToken), eq(TYPE_INPUT_METHOD),
-                eq(windowToken.mOptions));
+        verify(mWm.mWindowContextListenerController).registerWindowContainerListener(wpc,
+                clientToken, windowToken, TYPE_INPUT_METHOD, windowToken.mOptions,
+                false /* shouldDispatchConfigWhenRegistering */);
     }
 
     @Test
@@ -514,7 +514,7 @@
                 new InsetsSourceControl.Array(), new Rect(), new float[1]);
 
         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
-                any(), any(), anyInt(), any());
+                any(), any(), anyInt(), any(), anyBoolean());
     }
 
     @Test
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ed1c41f..314150b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9407,10 +9407,11 @@
             "missed_incoming_call_sms_pattern_string_array";
 
     /**
-     * Indicate the satellite services supported per provider by a carrier.
-     *
-     * Key is the PLMN of a satellite provider. Value should be an integer array of supported
-     * services with the following value:
+     * A PersistableBundle that contains a list of key-value pairs, where the values are integer
+     * arrays.
+     * <p>
+     * Keys are the PLMNs of satellite providers as strings and values are integer arrays of
+     * supported services with the following value:
      * <ul>
      * <li>1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}</li>
      * <li>2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}</li>
@@ -9419,19 +9420,35 @@
      * <li>5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}</li>
      * </ul>
      * <p>
-     * If this carrier config is not present, the overlay config
+     * An example config for two PLMNs "123411" and "123412":
+     * <pre>{@code
+     * <carrier_config>
+     *   <pbundle_as_map name="carrier_supported_satellite_services_per_provider_bundle">
+     *     <int-array name = "123411" num = "2">
+     *       <item value = "3"/>
+     *       <item value = "5"/>
+     *     </int-array>
+     *     <int-array name = "123412" num = "1">
+     *       <item value = "3"/>
+     *     </int-array>
+     *   </pbundle_as_map>
+     * </carrier_config>
+     * }</pre>
+     * <p>
+     * If this carrier config is not present, the device overlay config
      * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
-     * is present, the supported satellite services will be identified as follows:
+     * is present, the supported services associated with the PLMNs listed in the carrier config
+     * will override that of the device overlay config. The supported satellite services will be
+     * identified as follows:
      * <ul>
-     * <li>For the PLMN that exists in both provider supported satellite services and carrier
-     * supported satellite services, the supported services will be the intersection of the two
-     * sets.</li>
-     * <li>For the PLMN that is present in provider supported satellite services but not in carrier
-     * supported satellite services, the provider supported satellite services will be used.</li>
-     * <li>For the PLMN that is present in carrier supported satellite services but not in provider
-     * supported satellite services, the PLMN will be ignored.</li>
+     * <li>For each PLMN that exists only in the carrier provided satellite services, use the
+     * carrier provided services as the supported services.</li>
+     * <li>For each PLMN that is present only in the device provided satellite services, use the
+     * device provided services as the supported services.</li>
+     * <li>For each PLMN that is present in both the carrier provided and device provided satellite
+     * services, use the carrier provided services as the supported services.</li>
      * </ul>
-     *
+     * <p>
      * This config is empty by default.
      */
     public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index feef049..d41c019 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -81,6 +81,19 @@
             mCallback = callback;
         }
 
+        @Override
+        public void onServiceConnected() {
+            if (mCallback != null) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onServiceConnected());
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
         public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) {
             if (mCallback != null) {
                 final long token = Binder.clearCallingIdentity();
@@ -117,6 +130,7 @@
             }
         }
 
+        @Override
         public void onHotspotNetworkConnectionStatusChanged(
                 @NonNull HotspotNetworkConnectionStatus status) {
             if (mCallback != null) {
@@ -251,7 +265,6 @@
             synchronized (mProxyDataLock) {
                 mProxyMap.put(callback, proxy);
             }
-            callback.onServiceConnected();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in registerCallback", e);
             callback.onRegisterCallbackFailed(e);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
index 737aa6d..521f943 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -31,4 +31,5 @@
     oneway void onKnownNetworksUpdated(in List<KnownNetwork> networks);
     oneway void onKnownNetworkConnectionStatusChanged(in KnownNetworkConnectionStatus status);
     oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
+    oneway void onServiceConnected();
 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index 2bbe919..ebda6f1 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -276,6 +276,11 @@
 
     private void onRegisterCallback(ISharedConnectivityCallback callback) {
         mRemoteCallbackList.register(callback);
+        try {
+            callback.onServiceConnected();
+        } catch (RemoteException e) {
+            if (DEBUG) Log.w(TAG, "Exception in onRegisterCallback", e);
+        }
         if (mCountDownLatch != null) {
             mCountDownLatch.countDown();
         }