diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 6c8a8500e4e38c282491cefbf98e42b3a92e976a..d41df4f49d48fd3d2bf7274644fc96e67352022b 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -511,17 +511,31 @@ public class DatabaseUtils {
      */
     public static void appendEscapedSQLString(StringBuilder sb, String sqlString) {
         sb.append('\'');
-        if (sqlString.indexOf('\'') != -1) {
-            int length = sqlString.length();
-            for (int i = 0; i < length; i++) {
-                char c = sqlString.charAt(i);
-                if (c == '\'') {
-                    sb.append('\'');
+        int length = sqlString.length();
+        for (int i = 0; i < length; i++) {
+            char c = sqlString.charAt(i);
+            if (Character.isHighSurrogate(c)) {
+                if (i == length - 1) {
+                    continue;
+                }
+                if (Character.isLowSurrogate(sqlString.charAt(i + 1))) {
+                    // add them both
+                    sb.append(c);
+                    sb.append(sqlString.charAt(i + 1));
+                    continue;
+                } else {
+                    // this is a lone surrogate, skip it
+                    continue;
                 }
-                sb.append(c);
             }
-        } else
-            sb.append(sqlString);
+            if (Character.isLowSurrogate(c)) {
+                continue;
+            }
+            if (c == '\'') {
+                sb.append('\'');
+            }
+            sb.append(c);
+        }
         sb.append('\'');
     }
 
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 3da696ad0bc795bd33476676cb7fa83722e0a8c6..7fbaf1027af60e4cda7cbbdc7fa83fc3fc680214 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -882,10 +882,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
         }
 
         static Uri readFrom(Parcel parcel) {
+            final StringUri stringUri = new StringUri(parcel.readString8());
             return new OpaqueUri(
-                parcel.readString8(),
-                Part.readFrom(parcel),
-                Part.readFrom(parcel)
+                stringUri.parseScheme(),
+                stringUri.getSsp(),
+                stringUri.getFragmentPart()
             );
         }
 
@@ -895,9 +896,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
 
         public void writeToParcel(Parcel parcel, int flags) {
             parcel.writeInt(TYPE_ID);
-            parcel.writeString8(scheme);
-            ssp.writeTo(parcel);
-            fragment.writeTo(parcel);
+            parcel.writeString8(toString());
         }
 
         public boolean isHierarchical() {
@@ -1196,22 +1195,25 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
                 Part query, Part fragment) {
             this.scheme = scheme;
             this.authority = Part.nonNull(authority);
-            this.path = path == null ? PathPart.NULL : path;
+            this.path = generatePath(path);
             this.query = Part.nonNull(query);
             this.fragment = Part.nonNull(fragment);
         }
 
-        static Uri readFrom(Parcel parcel) {
-            final String scheme = parcel.readString8();
-            final Part authority = Part.readFrom(parcel);
+        private PathPart generatePath(PathPart originalPath) {
             // In RFC3986 the path should be determined based on whether there is a scheme or
             // authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3).
             final boolean hasSchemeOrAuthority =
                     (scheme != null && scheme.length() > 0) || !authority.isEmpty();
-            final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel);
-            final Part query = Part.readFrom(parcel);
-            final Part fragment = Part.readFrom(parcel);
-            return new HierarchicalUri(scheme, authority, path, query, fragment);
+            final PathPart newPath = hasSchemeOrAuthority ? PathPart.makeAbsolute(originalPath)
+                                                          : originalPath;
+            return newPath == null ? PathPart.NULL : newPath;
+        }
+
+        static Uri readFrom(Parcel parcel) {
+            final StringUri stringUri = new StringUri(parcel.readString8());
+            return new HierarchicalUri(stringUri.getScheme(), stringUri.getAuthorityPart(),
+                    stringUri.getPathPart(), stringUri.getQueryPart(), stringUri.getFragmentPart());
         }
 
         public int describeContents() {
@@ -1220,11 +1222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
 
         public void writeToParcel(Parcel parcel, int flags) {
             parcel.writeInt(TYPE_ID);
-            parcel.writeString8(scheme);
-            authority.writeTo(parcel);
-            path.writeTo(parcel);
-            query.writeTo(parcel);
-            fragment.writeTo(parcel);
+            parcel.writeString8(toString());
         }
 
         public boolean isHierarchical() {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5019b85ca503eef43630267b34dab731548712e5..a3d1be07c51c6097d672958a4186c40737f180d8 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -69,6 +69,7 @@ import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.inputmethod.ImeTracing;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.util.function.TriFunction;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -77,7 +78,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.BiFunction;
 
 /**
  * Implements {@link WindowInsetsController} on the client.
@@ -621,7 +621,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
     private final InsetsState mLastDispatchedState = new InsetsState();
 
     private final Rect mFrame = new Rect();
-    private final BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> mConsumerCreator;
+    private final TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer>
+            mConsumerCreator;
     private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
     private final InsetsSourceConsumer mImeSourceConsumer;
     private final Host mHost;
@@ -689,13 +690,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
 
                     // Don't change the indexes of the sources while traversing. Remove it later.
                     mPendingRemoveIndexes.add(index1);
-
-                    // Remove the consumer as well except the IME one. IME consumer should always
-                    // be there since we need to communicate with InputMethodManager no matter we
-                    // have the source or not.
-                    if (source1.getType() != ime()) {
-                        mSourceConsumers.remove(source1.getId());
-                    }
                 }
 
                 @Override
@@ -750,12 +744,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
             };
 
     public InsetsController(Host host) {
-        this(host, (controller, source) -> {
-            if (source.getType() == ime()) {
-                return new ImeInsetsSourceConsumer(source.getId(), controller.mState,
+        this(host, (controller, id, type) -> {
+            if (type == ime()) {
+                return new ImeInsetsSourceConsumer(id, controller.mState,
                         Transaction::new, controller);
             } else {
-                return new InsetsSourceConsumer(source.getId(), source.getType(), controller.mState,
+                return new InsetsSourceConsumer(id, type, controller.mState,
                         Transaction::new, controller);
             }
         }, host.getHandler());
@@ -763,7 +757,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
 
     @VisibleForTesting
     public InsetsController(Host host,
-            BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> consumerCreator,
+            TriFunction<InsetsController, Integer, Integer, InsetsSourceConsumer> consumerCreator,
             Handler handler) {
         mHost = host;
         mConsumerCreator = consumerCreator;
@@ -815,7 +809,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
         };
 
         // Make mImeSourceConsumer always non-null.
-        mImeSourceConsumer = getSourceConsumer(new InsetsSource(ID_IME, ime()));
+        mImeSourceConsumer = getSourceConsumer(ID_IME, ime());
     }
 
     @VisibleForTesting
@@ -893,7 +887,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
                     cancelledUserAnimationTypes[0] |= type;
                 }
             }
-            getSourceConsumer(source).updateSource(source, animationType);
+            final InsetsSourceConsumer consumer = mSourceConsumers.get(source.getId());
+            if (consumer != null) {
+                consumer.updateSource(source, animationType);
+            } else {
+                mState.addSource(source);
+            }
             existingTypes |= type;
             if (source.isVisible()) {
                 visibleTypes |= type;
@@ -997,8 +996,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
 
         @InsetsType int controllableTypes = 0;
         int consumedControlCount = 0;
-        final int[] showTypes = new int[1];
-        final int[] hideTypes = new int[1];
+        final @InsetsType int[] showTypes = new int[1];
+        final @InsetsType int[] hideTypes = new int[1];
 
         // Ensure to update all existing source consumers
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
@@ -1014,15 +1013,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
             consumer.setControl(control, showTypes, hideTypes);
         }
 
+        // Ensure to create source consumers if not available yet.
         if (consumedControlCount != mTmpControlArray.size()) {
-            // Whoops! The server sent us some controls without sending corresponding sources.
             for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
                 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
-                final InsetsSourceConsumer consumer = mSourceConsumers.get(control.getId());
-                if (consumer == null) {
-                    control.release(SurfaceControl::release);
-                    Log.e(TAG, control + " has no consumer.");
-                }
+                getSourceConsumer(control.getId(), control.getType())
+                        .setControl(control, showTypes, hideTypes);
             }
         }
 
@@ -1587,6 +1583,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
         if (type == ime()) {
             abortPendingImeControlRequest();
         }
+        if (consumer.getType() != ime()) {
+            // IME consumer should always be there since we need to communicate with
+            // InputMethodManager no matter we have the control or not.
+            mSourceConsumers.remove(consumer.getId());
+        }
     }
 
     private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
@@ -1640,21 +1641,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
     }
 
     @VisibleForTesting
-    public @NonNull InsetsSourceConsumer getSourceConsumer(InsetsSource source) {
-        final int sourceId = source.getId();
-        InsetsSourceConsumer consumer = mSourceConsumers.get(sourceId);
+    public @NonNull InsetsSourceConsumer getSourceConsumer(int id, int type) {
+        InsetsSourceConsumer consumer = mSourceConsumers.get(id);
         if (consumer != null) {
             return consumer;
         }
-        if (source.getType() == ime() && mImeSourceConsumer != null) {
+        if (type == ime() && mImeSourceConsumer != null) {
             // WindowInsets.Type.ime() should be only provided by one source.
             mSourceConsumers.remove(mImeSourceConsumer.getId());
             consumer = mImeSourceConsumer;
-            consumer.setId(sourceId);
+            consumer.setId(id);
         } else {
-            consumer = mConsumerCreator.apply(this, source);
+            consumer = mConsumerCreator.apply(this, id, type);
         }
-        mSourceConsumers.put(sourceId, consumer);
+        mSourceConsumers.put(id, consumer);
         return consumer;
     }
 
@@ -1663,8 +1663,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
         return mImeSourceConsumer;
     }
 
-    @VisibleForTesting
-    public void notifyVisibilityChanged() {
+    void notifyVisibilityChanged() {
         mHost.notifyInsetsChanged();
     }
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 467d720196b17801c0584a692e5fa076a94ef0da..34b288460a67bac11d8b3b85e66e104eb2931b30 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -149,9 +149,12 @@ public class InsetsSourceConsumer {
             // Check if we need to restore server visibility.
             final InsetsSource localSource = mState.peekSource(mId);
             final InsetsSource serverSource = mController.getLastDispatchedState().peekSource(mId);
-            if (localSource != null && serverSource != null
-                    && localSource.isVisible() != serverSource.isVisible()) {
-                localSource.setVisible(serverSource.isVisible());
+            final boolean localVisible = localSource != null && localSource.isVisible();
+            final boolean serverVisible = serverSource != null && serverSource.isVisible();
+            if (localSource != null) {
+                localSource.setVisible(serverVisible);
+            }
+            if (localVisible != serverVisible) {
                 mController.notifyVisibilityChanged();
             }
         } else {
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 0bc0878b73095e6c936656db754574e16f35ffbf..f97d41b6a122eaffe3a794c2b117c4019e688e07 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -42,6 +42,13 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
         return NULL;
     }
 
+    // b/274058082: Pass a copy of the key character map to avoid concurrent
+    // access
+    std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap();
+    if (map != nullptr) {
+        map = std::make_shared<KeyCharacterMap>(*map);
+    }
+
     ScopedLocalRef<jstring> descriptorObj(env,
             env->NewStringUTF(deviceInfo.getIdentifier().descriptor.c_str()));
     if (!descriptorObj.get()) {
@@ -61,8 +68,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
                                                                   : NULL));
 
     ScopedLocalRef<jobject> kcmObj(env,
-            android_view_KeyCharacterMap_create(env, deviceInfo.getId(),
-            deviceInfo.getKeyCharacterMap()));
+                                   android_view_KeyCharacterMap_create(env, deviceInfo.getId(),
+                                                                       map));
     if (!kcmObj.get()) {
         return NULL;
     }
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 89632a46267e6306f2e53dcce825e749242b0683..2a4ca79d997e835b2922e7f6c9551e42efdad9c9 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -25,8 +25,6 @@ import junit.framework.TestCase;
 
 import java.io.File;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
@@ -869,84 +867,90 @@ public class UriTest extends TestCase {
         return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
     }
 
-    /** Attempting to unparcel a legacy parcel format of Uri.{,Path}Part should fail. */
-    public void testUnparcelLegacyPart_fails() throws Exception {
-        assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$Part"));
-        assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$PathPart"));
-    }
-
-    private static void assertUnparcelLegacyPart_fails(Class partClass) throws Exception {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeInt(0 /* BOTH */);
-        parcel.writeString("encoded");
-        parcel.writeString("decoded");
-        parcel.setDataPosition(0);
-
-        Method readFromMethod = partClass.getDeclaredMethod("readFrom", Parcel.class);
-        readFromMethod.setAccessible(true);
-        try {
-            readFromMethod.invoke(null, parcel);
-            fail();
-        } catch (InvocationTargetException expected) {
-            Throwable targetException = expected.getTargetException();
-            // Check that the exception was thrown for the correct reason.
-            assertEquals("Unknown representation: 0", targetException.getMessage());
-        } finally {
-            parcel.recycle();
-        }
-    }
-
-    private Uri buildUriFromRawParcel(boolean argumentsEncoded,
+    private Uri buildUriFromParts(boolean argumentsEncoded,
                                       String scheme,
                                       String authority,
                                       String path,
                                       String query,
                                       String fragment) {
-        // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}).
-        final int representation = argumentsEncoded ? 1 : 2;
-        Parcel parcel = Parcel.obtain();
-        try {
-            parcel.writeInt(3);  // hierarchical
-            parcel.writeString8(scheme);
-            parcel.writeInt(representation);
-            parcel.writeString8(authority);
-            parcel.writeInt(representation);
-            parcel.writeString8(path);
-            parcel.writeInt(representation);
-            parcel.writeString8(query);
-            parcel.writeInt(representation);
-            parcel.writeString8(fragment);
-            parcel.setDataPosition(0);
-            return Uri.CREATOR.createFromParcel(parcel);
-        } finally {
-            parcel.recycle();
+        final Uri.Builder builder = new Uri.Builder();
+        builder.scheme(scheme);
+        if (argumentsEncoded) {
+            builder.encodedAuthority(authority);
+            builder.encodedPath(path);
+            builder.encodedQuery(query);
+            builder.encodedFragment(fragment);
+        } else {
+            builder.authority(authority);
+            builder.path(path);
+            builder.query(query);
+            builder.fragment(fragment);
         }
+        return builder.build();
     }
 
     public void testUnparcelMalformedPath() {
         // Regression tests for b/171966843.
 
         // Test cases with arguments encoded (covering testing `scheme` * `authority` options).
-        Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null);
+        Uri uri0 = buildUriFromParts(true, "https", "google.com", "@evil.com", null, null);
         assertEquals("https://google.com/@evil.com", uri0.toString());
-        Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x");
+        Uri uri1 = buildUriFromParts(true, null, "google.com", "@evil.com", "name=spark", "x");
         assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString());
-        Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null);
+        Uri uri2 = buildUriFromParts(true, "http:", null, "@evil.com", null, null);
         assertEquals("http::/@evil.com", uri2.toString());
-        Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null);
+        Uri uri3 = buildUriFromParts(true, null, null, "@evil.com", null, null);
         assertEquals("@evil.com", uri3.toString());
 
         // Test cases with arguments not encoded (covering testing `scheme` * `authority` options).
-        Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null);
+        Uri uriA = buildUriFromParts(false, "https", "google.com", "@evil.com", null, null);
         assertEquals("https://google.com/%40evil.com", uriA.toString());
-        Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null);
+        Uri uriB = buildUriFromParts(false, null, "google.com", "@evil.com", null, null);
         assertEquals("//google.com/%40evil.com", uriB.toString());
-        Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null);
+        Uri uriC = buildUriFromParts(false, "http:", null, "@evil.com", null, null);
         assertEquals("http::/%40evil.com", uriC.toString());
-        Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y");
+        Uri uriD = buildUriFromParts(false, null, null, "@evil.com", "name=spark", "y");
         assertEquals("%40evil.com?name%3Dspark#y", uriD.toString());
     }
 
+    public void testParsedUriFromStringEquality() {
+        Uri uri = buildUriFromParts(
+                true, "https", "google.com", "@evil.com", null, null);
+        assertEquals(uri, Uri.parse(uri.toString()));
+        Uri uri2 = buildUriFromParts(
+                true, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
+        assertEquals(uri2, Uri.parse(uri2.toString()));
+        Uri uri3 = buildUriFromParts(
+                false, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
+        assertEquals(uri3, Uri.parse(uri3.toString()));
+    }
+
+    public void testParceledUrisAreEqual() {
+        Uri opaqueUri = Uri.fromParts("fake://uri#", "ssp", "fragment");
+        Parcel parcel = Parcel.obtain();
+        try {
+            opaqueUri.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
+            Uri parsedUri = Uri.parse(postParcelUri.toString());
+            assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
+        } finally {
+            parcel.recycle();
+        }
+
+        Uri hierarchicalUri = new Uri.Builder().scheme("fake://uri#").authority("auth").build();
+        parcel = Parcel.obtain();
+        try {
+            hierarchicalUri.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
+            Uri parsedUri = Uri.parse(postParcelUri.toString());
+            assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
+        } finally {
+            parcel.recycle();
+        }
+    }
+
     public void testToSafeString() {
         checkToSafeString("tel:xxxxxx", "tel:Google");
         checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 06920524acfc54ede2e88929fa68fd6ffd22aeca..b8f0d5c82eaccd584e2041bde0d525471e2cdafd 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -131,10 +131,10 @@ public class InsetsControllerTest {
             mTestClock = new OffsettableClock();
             mTestHandler = new TestHandler(null, mTestClock);
             mTestHost = spy(new TestHost(mViewRoot));
-            mController = new InsetsController(mTestHost, (controller, source) -> {
-                if (source.getType() == ime()) {
-                    return new InsetsSourceConsumer(source.getId(), source.getType(),
-                            controller.getState(), Transaction::new, controller) {
+            mController = new InsetsController(mTestHost, (controller, id, type) -> {
+                if (type == ime()) {
+                    return new InsetsSourceConsumer(id, type, controller.getState(),
+                            Transaction::new, controller) {
 
                         private boolean mImeRequestedShow;
 
@@ -150,8 +150,8 @@ public class InsetsControllerTest {
                         }
                     };
                 } else {
-                    return new InsetsSourceConsumer(source.getId(), source.getType(),
-                            controller.getState(), Transaction::new, controller);
+                    return new InsetsSourceConsumer(id, type, controller.getState(),
+                            Transaction::new, controller);
                 }
             }, mTestHandler);
             final Rect rect = new Rect(5, 5, 5, 5);
@@ -182,7 +182,8 @@ public class InsetsControllerTest {
     @Test
     public void testControlsChanged() {
         mController.onControlsChanged(createSingletonControl(ID_STATUS_BAR, statusBars()));
-        assertNotNull(mController.getSourceConsumer(mStatusSource).getControl().getLeash());
+        assertNotNull(
+                mController.getSourceConsumer(ID_STATUS_BAR, statusBars()).getControl().getLeash());
         mController.addOnControllableInsetsChangedListener(
                 ((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
     }
@@ -194,7 +195,7 @@ public class InsetsControllerTest {
         mController.addOnControllableInsetsChangedListener(listener);
         mController.onControlsChanged(createSingletonControl(ID_STATUS_BAR, statusBars()));
         mController.onControlsChanged(new InsetsSourceControl[0]);
-        assertNull(mController.getSourceConsumer(mStatusSource).getControl());
+        assertNull(mController.getSourceConsumer(ID_STATUS_BAR, statusBars()).getControl());
         InOrder inOrder = Mockito.inOrder(listener);
         inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
         inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars()));
@@ -254,7 +255,7 @@ public class InsetsControllerTest {
         // only the original thread that created view hierarchy can touch its views
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
-            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+            mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             // When using the animation thread, this must not invoke onReady()
@@ -271,7 +272,7 @@ public class InsetsControllerTest {
         prepareControls();
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+            mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             mController.show(all());
@@ -284,7 +285,7 @@ public class InsetsControllerTest {
             mController.hide(all());
             mController.cancelExistingAnimations();
             assertEquals(0, mController.getRequestedVisibleTypes() & types);
-            mController.getSourceConsumer(mImeSource).onWindowFocusLost();
+            mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -294,14 +295,14 @@ public class InsetsControllerTest {
         InsetsSourceControl ime = createControl(ID_IME, ime());
         mController.onControlsChanged(new InsetsSourceControl[] { ime });
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
+            mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             mController.cancelExistingAnimations();
             assertTrue(isRequestedVisible(mController, ime()));
             mController.hide(ime(), true /* fromIme */, null /* statsToken */);
             mController.cancelExistingAnimations();
             assertFalse(isRequestedVisible(mController, ime()));
-            mController.getSourceConsumer(mImeSource).onWindowFocusLost();
+            mController.getSourceConsumer(ID_IME, ime()).onWindowFocusLost();
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -914,7 +915,7 @@ public class InsetsControllerTest {
             // Simulate IME insets is not controllable
             mController.onControlsChanged(new InsetsSourceControl[0]);
             final InsetsSourceConsumer imeInsetsConsumer =
-                    mController.getSourceConsumer(mImeSource);
+                    mController.getSourceConsumer(ID_IME, ime());
             assertNull(imeInsetsConsumer.getControl());
 
             // Verify IME requested visibility should be updated to IME consumer from controller.
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 988e69008008e7e02e2810f54a6666b773f79fbd..655cb4519d3c5519289d004e58bec2b0eb2cc82a 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -219,10 +219,10 @@ public class InsetsSourceConsumerTest {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             InsetsState state = new InsetsState();
             ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
-            InsetsController insetsController = new InsetsController(host, (controller, source) -> {
-                if (source.getType() == ime()) {
+            InsetsController insetsController = new InsetsController(host, (ic, id, type) -> {
+                if (type == ime()) {
                     return new InsetsSourceConsumer(ID_IME, ime(), state,
-                            () -> mMockTransaction, controller) {
+                            () -> mMockTransaction, ic) {
                         @Override
                         public int requestShow(boolean fromController,
                                 ImeTracker.Token statsToken) {
@@ -230,11 +230,9 @@ public class InsetsSourceConsumerTest {
                         }
                     };
                 }
-                return new InsetsSourceConsumer(source.getId(), source.getType(),
-                        controller.getState(), Transaction::new, controller);
+                return new InsetsSourceConsumer(id, type, ic.getState(), Transaction::new, ic);
             }, host.getHandler());
-            InsetsSource imeSource = new InsetsSource(ID_IME, ime());
-            InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(imeSource);
+            InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ID_IME, ime());
 
             // Initial IME insets source control with its leash.
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a044602f7ec02f59f57021fe851c7f4b034aff9c..b05507e7e128b8f5641a2bd9b99d2a2345a8da3b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -257,6 +257,7 @@ applications that come with the platform
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.ACCESS_INSTANT_APPS" />
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
         <permission name="android.permission.DELETE_CACHE_FILES"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.DUMP"/>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 16b35ffcabac69a1ddba3d6c738adb75801965c5..504dfaa2a1f5ab9e85b03e6e9af1c8450ee9b18c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -193,6 +193,7 @@ void CanvasContext::setHardwareBuffer(AHardwareBuffer* buffer) {
 void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
     ATRACE_CALL();
 
+    startHintSession();
     if (window) {
         mNativeSurface = std::make_unique<ReliableSurface>(window);
         mNativeSurface->init();
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 814ac4d90028d6dac427535477eb6be0b6253496..1f338ee523eb1325ffc1f42a529bed86ee27cc3d 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -93,8 +93,17 @@ HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
         : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
 
 HintSessionWrapper::~HintSessionWrapper() {
+    destroy();
+}
+
+void HintSessionWrapper::destroy() {
+    if (mHintSessionFuture.valid()) {
+        mHintSession = mHintSessionFuture.get();
+    }
     if (mHintSession) {
         gAPH_closeSessionFn(mHintSession);
+        mSessionValid = true;
+        mHintSession = nullptr;
     }
 }
 
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 24b8150dd489c7754a893fc8f65cd70b028422b6..bdb9959b1ca7e54d03d7e70fc984858820ef04db 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -37,6 +37,7 @@ public:
     void sendLoadResetHint();
     void sendLoadIncreaseHint();
     bool init();
+    void destroy();
 
 private:
     APerformanceHintSession* mHintSession = nullptr;
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index d2b21ae19162866f7a09357287e08490023de645..2c18342af94d97f0127db0c885bf2184e5cf5e28 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -807,10 +807,10 @@ public class RingtoneManager {
 
         return ringtoneUri;
     }
-    
+
     /**
      * Sets the {@link Uri} of the default sound for a given sound type.
-     * 
+     *
      * @param context A context used for querying.
      * @param type The type whose default sound should be set. One of
      *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4cb7f94f8e6d253e6fcfb87a6eaebc2b9e13a902..215a040c6a7237ca3b959489cbbaff69fbe862f4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3229,6 +3229,15 @@ public class SettingsProvider extends ContentProvider {
             return settingsState.getSettingLocked(name);
         }
 
+        private static boolean shouldExcludeSettingFromReset(Setting setting, String prefix) {
+            // If a prefix was specified, exclude settings whose names don't start with it.
+            if (prefix != null && !setting.getName().startsWith(prefix)) {
+                return true;
+            }
+            // Never reset SECURE_FRP_MODE, as it could be abused to bypass FRP via RescueParty.
+            return Global.SECURE_FRP_MODE.equals(setting.getName());
+        }
+
         public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag) {
             resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
@@ -3251,7 +3260,7 @@ public class SettingsProvider extends ContentProvider {
                         Setting setting = settingsState.getSettingLocked(name);
                         if (packageName.equals(setting.getPackageName())) {
                             if ((tag != null && !tag.equals(setting.getTag()))
-                                    || (prefix != null && !setting.getName().startsWith(prefix))) {
+                                    || shouldExcludeSettingFromReset(setting, prefix)) {
                                 continue;
                             }
                             if (settingsState.resetSettingLocked(name)) {
@@ -3272,7 +3281,7 @@ public class SettingsProvider extends ContentProvider {
                         Setting setting = settingsState.getSettingLocked(name);
                         if (!SettingsState.isSystemPackage(getContext(),
                                 setting.getPackageName())) {
-                            if (prefix != null && !setting.getName().startsWith(prefix)) {
+                            if (shouldExcludeSettingFromReset(setting, prefix)) {
                                 continue;
                             }
                             if (settingsState.resetSettingLocked(name)) {
@@ -3293,7 +3302,7 @@ public class SettingsProvider extends ContentProvider {
                         Setting setting = settingsState.getSettingLocked(name);
                         if (!SettingsState.isSystemPackage(getContext(),
                                 setting.getPackageName())) {
-                            if (prefix != null && !setting.getName().startsWith(prefix)) {
+                            if (shouldExcludeSettingFromReset(setting, prefix)) {
                                 continue;
                             }
                             if (setting.isDefaultFromSystem()) {
@@ -3318,7 +3327,7 @@ public class SettingsProvider extends ContentProvider {
                     for (String name : settingsState.getSettingNamesLocked()) {
                         Setting setting = settingsState.getSettingLocked(name);
                         boolean someSettingChanged = false;
-                        if (prefix != null && !setting.getName().startsWith(prefix)) {
+                        if (shouldExcludeSettingFromReset(setting, prefix)) {
                             continue;
                         }
                         if (setting.isDefaultFromSystem()) {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index eaf0dcb9b4e797a07e5c2c058c3bbfe260b5024a..a945c33bc20a1e3c4012c24be595e42d503e7ee5 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -464,6 +464,31 @@ public class SettingsProviderTest extends BaseSettingsProviderTest {
         }
     }
 
+    // To prevent FRP bypasses, the SECURE_FRP_MODE setting should not be reset when all other
+    // settings are reset.  But it should still be possible to explicitly set its value.
+    @Test
+    public void testSecureFrpModeSettingCannotBeReset() throws Exception {
+        final String name = Settings.Global.SECURE_FRP_MODE;
+        final String origValue = getSetting(SETTING_TYPE_GLOBAL, name);
+        setSettingViaShell(SETTING_TYPE_GLOBAL, name, "1", false);
+        try {
+            assertEquals("1", getSetting(SETTING_TYPE_GLOBAL, name));
+            for (int type : new int[] { SETTING_TYPE_GLOBAL, SETTING_TYPE_SECURE }) {
+                resetSettingsViaShell(type, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+                resetSettingsViaShell(type, Settings.RESET_MODE_UNTRUSTED_CHANGES);
+                resetSettingsViaShell(type, Settings.RESET_MODE_TRUSTED_DEFAULTS);
+            }
+            // The value should still be "1".  It should not have been reset to null.
+            assertEquals("1", getSetting(SETTING_TYPE_GLOBAL, name));
+            // It should still be possible to explicitly set the value to "0".
+            setSettingViaShell(SETTING_TYPE_GLOBAL, name, "0", false);
+            assertEquals("0", getSetting(SETTING_TYPE_GLOBAL, name));
+        } finally {
+            setSettingViaShell(SETTING_TYPE_GLOBAL, name, origValue, false);
+            assertEquals(origValue, getSetting(SETTING_TYPE_GLOBAL, name));
+        }
+    }
+
     private void doTestQueryStringInBracketsViaProviderApiForType(int type) {
         // Make sure we have a clean slate.
         deleteStringViaProviderApi(type, FAKE_SETTING_NAME);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 56e0643b1e20790a1142fd5c2b8d3e6bf64e44de..ee9883b0b0afb6e19f74bb1a6b4c80aa890ce1b2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,6 +320,7 @@
 
 
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+    <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <uses-permission android:name="android.permission.SUSPEND_APPS" />
     <uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
     <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 885b53d7e5d607daca15420e35ea23965ad5d487..b4fb887016343ad544c6139ac127fc6ad18c46f2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -330,7 +330,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
             }
         });
 
-        in.setAlpha(0);
         in.setVisibility(View.VISIBLE);
         mClockInAnim = new AnimatorSet();
         mClockInAnim.setDuration(CLOCK_IN_MILLIS);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2b71728f3391cc2d02100198155713ee69b507a2..f446e523afa964e94ad0a0876be181a4acc5b424 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -786,8 +786,6 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
 
     void reloadColors() {
         mViewMode.reloadColors();
-        setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.materialColorSurface));
     }
 
     /** Handles density or font scale changes. */
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 3eb58bba1ca42a8f6c7db049406926d32120c0e4..ec76f433b23b1fcb45fd128f771156fd8ad35aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -38,6 +38,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 import com.android.systemui.controls.ControlInterface
+import com.android.systemui.controls.ui.CanUseIconPredicate
 import com.android.systemui.controls.ui.RenderInfo
 
 private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
@@ -51,7 +52,8 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
  * @property elevation elevation of each control view
  */
 class ControlAdapter(
-    private val elevation: Float
+    private val elevation: Float,
+    private val currentUserId: Int,
 ) : RecyclerView.Adapter<Holder>() {
 
     companion object {
@@ -107,7 +109,8 @@ class ControlAdapter(
                         background = parent.context.getDrawable(
                                 R.drawable.control_background_ripple)
                     },
-                    model?.moveHelper // Indicates that position information is needed
+                    currentUserId,
+                    model?.moveHelper, // Indicates that position information is needed
                 ) { id, favorite ->
                     model?.changeFavoriteStatus(id, favorite)
                 }
@@ -212,8 +215,9 @@ private class ZoneHolder(view: View) : Holder(view) {
  */
 internal class ControlHolder(
     view: View,
+    currentUserId: Int,
     val moveHelper: ControlsModel.MoveHelper?,
-    val favoriteCallback: ModelFavoriteChanger
+    val favoriteCallback: ModelFavoriteChanger,
 ) : Holder(view) {
     private val favoriteStateDescription =
         itemView.context.getString(R.string.accessibility_control_favorite)
@@ -228,6 +232,7 @@ internal class ControlHolder(
         visibility = View.VISIBLE
     }
 
+    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
     private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
         this::stateDescription,
         this::getLayoutPosition,
@@ -287,7 +292,9 @@ internal class ControlHolder(
         val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
 
         icon.imageTintList = null
-        ci.customIcon?.let {
+        ci.customIcon
+                ?.takeIf(canUseIconPredicate)
+                ?.let {
             icon.setImageIcon(it)
         } ?: run {
             icon.setImageDrawable(ri.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 8e41974b9accafbcd34d13c23a0f25b7bc1b3e00..b387e4a66bd2b1a0771fb3e8beb6e281e3bcb22c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -250,7 +250,7 @@ open class ControlsEditingActivity @Inject constructor(
         val elevation = resources.getFloat(R.dimen.control_card_elevation)
         val recyclerView = requireViewById<RecyclerView>(R.id.list)
         recyclerView.alpha = 0.0f
-        val adapter = ControlAdapter(elevation).apply {
+        val adapter = ControlAdapter(elevation, userTracker.userId).apply {
             registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
                 var hasAnimated = false
                 override fun onChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 21051e7abe1f622e1be47bef566800ff27d46b3c..da571b3af086bdfafcd81ff0e3208b20d83246bd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -197,7 +197,7 @@ open class ControlsFavoritingActivity @Inject constructor(
                 }
 
                 executor.execute {
-                    structurePager.adapter = StructureAdapter(listOfStructures)
+                    structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId)
                     structurePager.setCurrentItem(structureIndex)
                     if (error) {
                         statusText.text = resources.getString(R.string.controls_favorite_load_error,
@@ -243,7 +243,7 @@ open class ControlsFavoritingActivity @Inject constructor(
         structurePager.alpha = 0.0f
         pageIndicator.alpha = 0.0f
         structurePager.apply {
-            adapter = StructureAdapter(emptyList())
+            adapter = StructureAdapter(emptyList(), userTracker.userId)
             registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                 override fun onPageSelected(position: Int) {
                     super.onPageSelected(position)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
index 747bcbe1c2297c4b0e0542f6a6acb2983abb8876..5977d379acde26b66fbb2976e59887fbaba563df 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
@@ -24,13 +24,15 @@ import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 
 class StructureAdapter(
-    private val models: List<StructureContainer>
+    private val models: List<StructureContainer>,
+    private val currentUserId: Int,
 ) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() {
 
     override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder {
         val layoutInflater = LayoutInflater.from(parent.context)
         return StructureHolder(
-            layoutInflater.inflate(R.layout.controls_structure_page, parent, false)
+            layoutInflater.inflate(R.layout.controls_structure_page, parent, false),
+            currentUserId,
         )
     }
 
@@ -40,7 +42,8 @@ class StructureAdapter(
         holder.bind(models[index].model)
     }
 
-    class StructureHolder(view: View) : RecyclerView.ViewHolder(view) {
+    class StructureHolder(view: View, currentUserId: Int) :
+            RecyclerView.ViewHolder(view) {
 
         private val recyclerView: RecyclerView
         private val controlAdapter: ControlAdapter
@@ -48,7 +51,7 @@ class StructureAdapter(
         init {
             recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll)
             val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation)
-            controlAdapter = ControlAdapter(elevation)
+            controlAdapter = ControlAdapter(elevation, currentUserId)
             setUpRecyclerView()
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt
new file mode 100644
index 0000000000000000000000000000000000000000..61c21237144d5c410f040f1402ba450b5c9d83f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.controls.ui
+
+import android.content.ContentProvider
+import android.graphics.drawable.Icon
+
+class CanUseIconPredicate(private val currentUserId: Int) : (Icon) -> Boolean {
+
+    override fun invoke(icon: Icon): Boolean =
+        if (icon.type == Icon.TYPE_URI || icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+            ContentProvider.getUserIdFromUri(icon.uri, currentUserId) == currentUserId
+        } else {
+            true
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 992c894b6b21b550f8cf290593bae7fac81dd83a..abe342320858251d257ab11107d9d46622caf55f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -68,7 +68,8 @@ class ControlViewHolder(
     val bgExecutor: DelayableExecutor,
     val controlActionCoordinator: ControlActionCoordinator,
     val controlsMetricsLogger: ControlsMetricsLogger,
-    val uid: Int
+    val uid: Int,
+    val currentUserId: Int,
 ) {
 
     companion object {
@@ -85,29 +86,9 @@ class ControlViewHolder(
         private val ATTR_DISABLED = intArrayOf(-android.R.attr.state_enabled)
         const val MIN_LEVEL = 0
         const val MAX_LEVEL = 10000
-
-        fun findBehaviorClass(
-            status: Int,
-            template: ControlTemplate,
-            deviceType: Int
-        ): Supplier<out Behavior> {
-            return when {
-                status != Control.STATUS_OK -> Supplier { StatusBehavior() }
-                template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
-                template is ThumbnailTemplate -> Supplier { ThumbnailBehavior() }
-
-                // Required for legacy support, or where cameras do not use the new template
-                deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
-                template is ToggleTemplate -> Supplier { ToggleBehavior() }
-                template is StatelessTemplate -> Supplier { TouchBehavior() }
-                template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
-                template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
-                template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
-                else -> Supplier { DefaultBehavior() }
-            }
-        }
     }
 
+    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
     private val toggleBackgroundIntensity: Float = layout.context.resources
             .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
     private var stateAnimator: ValueAnimator? = null
@@ -147,6 +128,27 @@ class ControlViewHolder(
         status.setSelected(true)
     }
 
+    fun findBehaviorClass(
+            status: Int,
+            template: ControlTemplate,
+            deviceType: Int
+    ): Supplier<out Behavior> {
+        return when {
+            status != Control.STATUS_OK -> Supplier { StatusBehavior() }
+            template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
+            template is ThumbnailTemplate -> Supplier { ThumbnailBehavior(currentUserId) }
+
+            // Required for legacy support, or where cameras do not use the new template
+            deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
+            template is ToggleTemplate -> Supplier { ToggleBehavior() }
+            template is StatelessTemplate -> Supplier { TouchBehavior() }
+            template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
+            template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
+            template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
+            else -> Supplier { DefaultBehavior() }
+        }
+    }
+
     fun bindData(cws: ControlWithState, isLocked: Boolean) {
         // If an interaction is in progress, the update may visually interfere with the action the
         // action the user wants to make. Don't apply the update, and instead assume a new update
@@ -473,7 +475,9 @@ class ControlViewHolder(
 
         status.setTextColor(color)
 
-        control?.getCustomIcon()?.let {
+        control?.customIcon
+                ?.takeIf(canUseIconPredicate)
+                ?.let {
             icon.setImageIcon(it)
             icon.imageTintList = it.tintList
         } ?: run {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 776b336e7b61ea942b621b9dd452421bbe194bcb..631ed3c26496c3571fa87bf5b1cfd6b11ae69461 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -690,7 +690,8 @@ class ControlsUiControllerImpl @Inject constructor (
                     bgExecutor,
                     controlActionCoordinator,
                     controlsMetricsLogger,
-                    selected.uid
+                    selected.uid,
+                    controlsController.get().currentUserId,
                 )
                 cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
                 controlViewsById.put(key, cvh)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index a7dc09bb17e567cce208fd2dbc670d84676594a5..39d69704d8171e03869f52c342dc730bac0204f1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -63,7 +63,7 @@ class TemperatureControlBehavior : Behavior {
             // interactions (touch, range)
             subBehavior = cvh.bindBehavior(
                 subBehavior,
-                ControlViewHolder.findBehaviorClass(
+                cvh.findBehaviorClass(
                     control.status,
                     subTemplate,
                     control.deviceType
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
index 86f093d1aaf4fc9f2b0a86726ef179b15a79422e..0b57e792f9f76d09beeb634c223afeaedc986cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
@@ -33,7 +33,7 @@ import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
  * Supports display of static images on the background of the tile. When marked active, the title
  * and subtitle will not be visible. To be used with {@link Thumbnailtemplate} only.
  */
-class ThumbnailBehavior : Behavior {
+class ThumbnailBehavior(currentUserId: Int) : Behavior {
     lateinit var template: ThumbnailTemplate
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
@@ -42,6 +42,7 @@ class ThumbnailBehavior : Behavior {
     private var shadowRadius: Float = 0f
     private var shadowColor: Int = 0
 
+    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
     private val enabled: Boolean
         get() = template.isActive()
 
@@ -80,7 +81,9 @@ class ThumbnailBehavior : Behavior {
             cvh.status.setShadowLayer(shadowOffsetX, shadowOffsetY, shadowRadius, shadowColor)
 
             cvh.bgExecutor.execute {
-                val drawable = template.getThumbnail().loadDrawable(cvh.context)
+                val drawable = template.thumbnail
+                        ?.takeIf(canUseIconPredicate)
+                        ?.loadDrawable(cvh.context)
                 cvh.uiExecutor.execute {
                     val radius = cvh.context.getResources()
                         .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 25ecf1a424e04002ad844e0c27b49c848f28ae85..c42a6dc5b6f2cd520d68e38c11014b8a790617b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1509,6 +1509,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
             mColors.setSupportsDarkText(
                     ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5);
         }
+
+        int surface = Utils.getColorAttr(mScrimBehind.getContext(),
+                com.android.internal.R.attr.materialColorSurface).getDefaultColor();
+        for (ScrimState state : ScrimState.values()) {
+            state.setSurfaceColor(surface);
+        }
+
         mNeedsDrawableColorUpdate = true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7b2028310a84ed292ae6e798404093db36698002..e3b65ab27f4852708f7f88abff711a3c305d2972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -122,11 +122,19 @@ public enum ScrimState {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
-            mBehindTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT;
+            mBehindTint = mClipQsScrim ? Color.BLACK : mSurfaceColor;
             mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
             mNotifTint = Color.TRANSPARENT;
             mFrontAlpha = 0f;
         }
+
+        @Override
+        public void setSurfaceColor(int surfaceColor) {
+            super.setSurfaceColor(surfaceColor);
+            if (!mClipQsScrim) {
+                mBehindTint = mSurfaceColor;
+            }
+        }
     },
 
     /**
@@ -295,6 +303,7 @@ public enum ScrimState {
     int mFrontTint = Color.TRANSPARENT;
     int mBehindTint = Color.TRANSPARENT;
     int mNotifTint = Color.TRANSPARENT;
+    int mSurfaceColor = Color.TRANSPARENT;
 
     boolean mAnimateChange = true;
     float mAodFrontScrimAlpha;
@@ -409,6 +418,10 @@ public enum ScrimState {
         mDefaultScrimAlpha = defaultScrimAlpha;
     }
 
+    public void setSurfaceColor(int surfaceColor) {
+        mSurfaceColor = surfaceColor;
+    }
+
     public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
         mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bcf3b0cbfc867161f6e5ec49b8e10da5204745d9..24987abd7a8521f119fb7c8211c1b05573a61c02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -240,8 +240,10 @@ public class StatusBarWindowController {
                     Insets.of(0, safeTouchRegionHeight, 0, 0));
         }
         lp.providedInsets = new InsetsFrameProvider[] {
-                new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()),
-                new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()),
+                new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars())
+                        .setInsetsSize(getInsets(height)),
+                new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement())
+                        .setInsetsSize(getInsets(height)),
                 gestureInsetsProvider
         };
         return lp;
@@ -306,12 +308,37 @@ public class StatusBarWindowController {
         mLpChanged.height =
                 state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : mBarHeight;
         for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+            int height = SystemBarUtils.getStatusBarHeightForRotation(mContext, rot);
             mLpChanged.paramsForRotation[rot].height =
-                    state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT :
-                    SystemBarUtils.getStatusBarHeightForRotation(mContext, rot);
+                    state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : height;
+            // The status bar height could change at runtime if one display has a cutout while
+            // another doesn't (like some foldables). It could also change when using debug cutouts.
+            // So, we need to re-fetch the height and re-apply it to the insets each time to avoid
+            // bugs like b/290300359.
+            InsetsFrameProvider[] providers = mLpChanged.paramsForRotation[rot].providedInsets;
+            if (providers != null) {
+                for (InsetsFrameProvider provider : providers) {
+                    provider.setInsetsSize(getInsets(height));
+                }
+            }
         }
     }
 
+    /**
+     * Get the insets that should be applied to the status bar window given the current status bar
+     * height.
+     *
+     * The status bar window height can sometimes be full-screen (see {@link #applyHeight(State)}.
+     * However, the status bar *insets* should *not* be full-screen, because this would prevent apps
+     * from drawing any content and can cause animations to be cancelled (see b/283958440). Instead,
+     * the status bar insets should always be equal to the space occupied by the actual status bar
+     * content -- setting the insets correctly will prevent window manager from unnecessarily
+     * re-drawing this window and other windows. This method provides the correct insets.
+     */
+    private Insets getInsets(int height) {
+        return Insets.of(0, height, 0, 0);
+    }
+
     private void apply(State state) {
         if (!mIsAttached) {
             return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bfdb9231a9f8ce004e9da52895d4cf90ab66aed5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.controls.ui
+
+import android.content.ContentProvider
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CanUseIconPredicateTest : SysuiTestCase() {
+
+    private companion object {
+        const val USER_ID_1 = 1
+        const val USER_ID_2 = 2
+    }
+
+    val underTest: CanUseIconPredicate = CanUseIconPredicate(USER_ID_1)
+
+    @Test
+    fun testReturnsFalseForDifferentUser() {
+        val user2Icon =
+            Icon.createWithContentUri(
+                ContentProvider.createContentUriForUser(
+                    Uri.parse("content://test"),
+                    UserHandle.of(USER_ID_2)
+                )
+            )
+
+        assertThat(underTest.invoke(user2Icon)).isFalse()
+    }
+
+    @Test
+    fun testReturnsTrueForCorrectUser() {
+        val user1Icon =
+            Icon.createWithContentUri(
+                ContentProvider.createContentUriForUser(
+                    Uri.parse("content://test"),
+                    UserHandle.of(USER_ID_1)
+                )
+            )
+
+        assertThat(underTest.invoke(user1Icon)).isTrue()
+    }
+
+    @Test
+    fun testReturnsTrueForUriWithoutUser() {
+        val uriIcon = Icon.createWithContentUri(Uri.parse("content://test"))
+
+        assertThat(underTest.invoke(uriIcon)).isTrue()
+    }
+
+    @Test
+    fun testReturnsTrueForNonUriIcon() {
+        val bitmapIcon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
+
+        assertThat(underTest.invoke(bitmapIcon)).isTrue()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 1461a3fbeeb69a0d65bd4c5f4fbaee82de012eb1..2ae342a5cfa50253da4cc4d3a7ec5d38501fa850 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -66,7 +66,8 @@ class ControlViewHolderTest : SysuiTestCase() {
                     FakeExecutor(clock),
                     mock(ControlActionCoordinator::class.java),
                     mock(ControlsMetricsLogger::class.java),
-                    uid = 100
+                    uid = 100,
+                    0,
             )
 
             val cws = ControlWithState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index a9ed17531926590c6bdc5bd64a96ce0fbb66c34c..7fc02280354f3b0d2d845b624a248666384ed09c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -44,6 +44,9 @@ import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
 
 import android.animation.Animator;
 import android.app.AlarmManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
@@ -121,6 +124,7 @@ public class ScrimControllerTest extends SysuiTestCase {
     private int mScrimVisibility;
     private boolean mAlwaysOnEnabled;
     private TestableLooper mLooper;
+    private Context mContext;
     @Mock private AlarmManager mAlarmManager;
     @Mock private DozeParameters mDozeParameters;
     @Mock private LightBarController mLightBarController;
@@ -134,6 +138,7 @@ public class ScrimControllerTest extends SysuiTestCase {
     @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock private CoroutineDispatcher mMainDispatcher;
+    @Mock private TypedArray mMockTypedArray;
 
     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
     //   event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@@ -182,10 +187,11 @@ public class ScrimControllerTest extends SysuiTestCase {
             mNumEnds = 0;
             mNumCancels = 0;
         }
-    };
+    }
 
     private AnimatorListener mAnimatorListener = new AnimatorListener();
 
+    private int mSurfaceColor = 0x112233;
 
     private void finishAnimationsImmediately() {
         // Execute code that will trigger animations.
@@ -214,10 +220,17 @@ public class ScrimControllerTest extends SysuiTestCase {
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        mContext = spy(getContext());
+        when(mContext.obtainStyledAttributes(
+                new int[]{com.android.internal.R.attr.materialColorSurface}))
+                .thenReturn(mMockTypedArray);
+
+        when(mMockTypedArray.getColorStateList(anyInt()))
+                .thenAnswer((invocation) -> ColorStateList.valueOf(mSurfaceColor));
 
-        mScrimBehind = spy(new ScrimView(getContext()));
-        mScrimInFront = new ScrimView(getContext());
-        mNotificationsScrim = new ScrimView(getContext());
+        mScrimBehind = spy(new ScrimView(mContext));
+        mScrimInFront = new ScrimView(mContext);
+        mNotificationsScrim = new ScrimView(mContext);
         mAlwaysOnEnabled = true;
         mLooper = TestableLooper.get(this);
         DejankUtils.setImmediate(true);
@@ -577,7 +590,7 @@ public class ScrimControllerTest extends SysuiTestCase {
         mScrimController.transitionTo(BOUNCER);
         finishAnimationsImmediately();
         // Front scrim should be transparent
-        // Back scrim should be visible without tint
+        // Back scrim should be visible and tinted to the surface color
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT,
@@ -585,9 +598,31 @@ public class ScrimControllerTest extends SysuiTestCase {
 
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, false,
+                mScrimBehind, true,
                 mNotificationsScrim, false
         ));
+
+        assertScrimTint(mScrimBehind, mSurfaceColor);
+    }
+
+    @Test
+    public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() {
+        assertEquals(BOUNCER.getBehindTint(), 0x112233);
+        mSurfaceColor = 0x223344;
+        mConfigurationController.notifyThemeChanged();
+        assertEquals(BOUNCER.getBehindTint(), 0x223344);
+    }
+
+    @Test
+    public void onThemeChangeWhileClipQsScrim_bouncerBehindTint_remainsBlack() {
+        mScrimController.setClipsQsScrim(true);
+        mScrimController.transitionTo(BOUNCER);
+        finishAnimationsImmediately();
+
+        assertEquals(BOUNCER.getBehindTint(), Color.BLACK);
+        mSurfaceColor = 0x223344;
+        mConfigurationController.notifyThemeChanged();
+        assertEquals(BOUNCER.getBehindTint(), Color.BLACK);
     }
 
     @Test
@@ -619,16 +654,17 @@ public class ScrimControllerTest extends SysuiTestCase {
 
         finishAnimationsImmediately();
         // Front scrim should be transparent
-        // Back scrim should be visible without tint
+        // Back scrim should be visible and has a tint of surfaceColor
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT,
                 mScrimBehind, OPAQUE));
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, false,
+                mScrimBehind, true,
                 mNotificationsScrim, false
         ));
+        assertScrimTint(mScrimBehind, mSurfaceColor);
     }
 
     @Test
@@ -1809,6 +1845,13 @@ public class ScrimControllerTest extends SysuiTestCase {
         assertEquals(message, hasTint, scrim.getTint() != Color.TRANSPARENT);
     }
 
+    private void assertScrimTint(ScrimView scrim, int expectedTint) {
+        String message = "Tint test failed with expected scrim tint: "
+                + Integer.toHexString(expectedTint) + " and actual tint: "
+                + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim);
+        assertEquals(message, expectedTint, scrim.getTint(), 0.1);
+    }
+
     private String getScrimName(ScrimView scrim) {
         if (scrim == mScrimInFront) {
             return "front";
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 82af38200166954f4ac87f6d68d49569f33c7c53..7557071d0d4b8645c4035e15724dc61a173c4695 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -20,6 +20,8 @@ import static com.android.server.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
@@ -40,6 +42,7 @@ import android.view.View;
 import android.view.WindowManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
@@ -50,6 +53,8 @@ import java.lang.ref.WeakReference;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 
 public final class Helper {
 
@@ -83,6 +88,44 @@ public final class Helper {
         throw new UnsupportedOperationException("contains static members only");
     }
 
+    private static boolean checkRemoteViewUriPermissions(
+            @UserIdInt int userId, @NonNull RemoteViews rView) {
+        final AtomicBoolean permissionsOk = new AtomicBoolean(true);
+
+        rView.visitUris(uri -> {
+            int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri);
+            boolean allowed = uriOwnerId == userId;
+            permissionsOk.set(allowed && permissionsOk.get());
+        });
+
+        return permissionsOk.get();
+    }
+
+    /**
+     * Checks the URI permissions of the remote view,
+     * to see if the current userId is able to access it.
+     *
+     * Returns the RemoteView that is passed if user is able, null otherwise.
+     *
+     * TODO: instead of returning a null remoteview when
+     * the current userId cannot access an URI,
+     * return a new RemoteView with the URI removed.
+     */
+    public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) {
+        if (rView == null) return null;
+
+        int userId = ActivityManager.getCurrentUser();
+
+        boolean ok = checkRemoteViewUriPermissions(userId, rView);
+        if (!ok) {
+            Slog.w(TAG,
+                    "sanitizeRemoteView() user: " + userId
+                    + " tried accessing resource that does not belong to them");
+        }
+        return (ok ? rView : null);
+    }
+
+
     @Nullable
     static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
         if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index dbeb624bd202c2d684c9cd44ace1afbfd721c387..fa414e3b172b330f85608e85b78a9f977b89a791 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -53,6 +53,7 @@ import android.widget.TextView;
 
 import com.android.internal.R;
 import com.android.server.autofill.AutofillManagerService;
+import com.android.server.autofill.Helper;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -208,7 +209,8 @@ final class DialogFillUi {
     }
 
     private void setHeader(View decor, FillResponse response) {
-        final RemoteViews presentation = response.getDialogHeader();
+        final RemoteViews presentation =
+                Helper.sanitizeRemoteView(response.getDialogHeader());
         if (presentation == null) {
             return;
         }
@@ -243,9 +245,10 @@ final class DialogFillUi {
     }
 
     private void initialAuthenticationLayout(View decor, FillResponse response) {
-        RemoteViews presentation = response.getDialogPresentation();
+        RemoteViews presentation = Helper.sanitizeRemoteView(
+                response.getDialogPresentation());
         if (presentation == null) {
-            presentation = response.getPresentation();
+            presentation = Helper.sanitizeRemoteView(response.getPresentation());
         }
         if (presentation == null) {
             throw new RuntimeException("No presentation for fill dialog authentication");
@@ -289,7 +292,8 @@ final class DialogFillUi {
             final Dataset dataset = response.getDatasets().get(i);
             final int index = dataset.getFieldIds().indexOf(focusedViewId);
             if (index >= 0) {
-                RemoteViews presentation = dataset.getFieldDialogPresentation(index);
+                RemoteViews presentation = Helper.sanitizeRemoteView(
+                        dataset.getFieldDialogPresentation(index));
                 if (presentation == null) {
                     if (sDebug) {
                         Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 129ce72e037dfa6ba9bee9577729e0b9469177a2..cdfe7bb4f4a78126309f6aba600ea8ba4655d572 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -148,8 +148,9 @@ final class FillUi {
 
         final LayoutInflater inflater = LayoutInflater.from(mContext);
 
-        final RemoteViews headerPresentation = response.getHeader();
-        final RemoteViews footerPresentation = response.getFooter();
+        final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader());
+        final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter());
+
         final ViewGroup decor;
         if (mFullScreen) {
             decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
@@ -227,6 +228,9 @@ final class FillUi {
             ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
             final View content;
             try {
+                if (Helper.sanitizeRemoteView(response.getPresentation()) == null) {
+                    throw new RuntimeException("Permission error accessing RemoteView");
+                }
                 content = response.getPresentation().applyWithTheme(
                         mContext, decor, interceptionHandler, mThemeId);
                 container.addView(content);
@@ -306,7 +310,8 @@ final class FillUi {
                 final Dataset dataset = response.getDatasets().get(i);
                 final int index = dataset.getFieldIds().indexOf(focusedViewId);
                 if (index >= 0) {
-                    final RemoteViews presentation = dataset.getFieldPresentation(index);
+                    final RemoteViews presentation = Helper.sanitizeRemoteView(
+                            dataset.getFieldPresentation(index));
                     if (presentation == null) {
                         Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
                                 + "service didn't provide a presentation for it on " + dataset);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index f035d076427908284c22331c4c66d7e1c964b315..70382f1d5274b6fd298581eff981f0b55be921a9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -384,8 +384,7 @@ final class SaveUi {
             return false;
         }
         writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION);
-
-        final RemoteViews template = customDescription.getPresentation();
+        final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation());
         if (template == null) {
             Slog.w(TAG, "No remote view on custom description");
             return false;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d5f41a1090422d92026a868e5a8bd9ffeaf4571a..a7107161777cdf7728e27fdeafb637dfd0f36900 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2414,7 +2414,7 @@ public final class ActiveServices {
                     // Even if the service is already a FGS, we need to update the notification,
                     // so we need to call it again.
                     signalForegroundServiceObserversLocked(r);
-                    r.postNotification();
+                    r.postNotification(true);
                     if (r.app != null) {
                         updateServiceForegroundLocked(psr, true);
                     }
@@ -2472,7 +2472,7 @@ public final class ActiveServices {
                 } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                     // if it's been deferred, force to visibility
                     if (!r.mFgsNotificationShown) {
-                        r.postNotification();
+                        r.postNotification(false);
                     }
                     dropFgsNotificationStateLocked(r);
                     if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
@@ -2916,7 +2916,7 @@ public final class ActiveServices {
                         // in the interval, so we lazy check whether we still need to show
                         // the notification.
                         if (r.isForeground && r.app != null) {
-                            r.postNotification();
+                            r.postNotification(true);
                             r.mFgsNotificationShown = true;
                         } else {
                             if (DEBUG_FOREGROUND_SERVICE) {
@@ -5339,7 +5339,7 @@ public final class ActiveServices {
             thread.scheduleCreateService(r, r.serviceInfo,
                     null /* compatInfo (unused but need to keep method signature) */,
                     app.mState.getReportedProcState());
-            r.postNotification();
+            r.postNotification(false);
             created = true;
         } catch (DeadObjectException e) {
             Slog.w(TAG, "Application dead when creating service " + r);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 50fe6d71d26e0ef781ff32db4ea3783d7a64cdb9..b9914faa469aadba5b121b8d0b3b055562d766dd 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1212,7 +1212,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
         });
     }
 
-    public void postNotification() {
+    public void postNotification(boolean byForegroundService) {
         if (isForeground && foregroundNoti != null && app != null) {
             final int appUid = appInfo.uid;
             final int appPid = app.getPid();
@@ -1320,7 +1320,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
                         }
                         nm.enqueueNotification(localPackageName, localPackageName,
                                 appUid, appPid, null, localForegroundId, localForegroundNoti,
-                                userId);
+                                userId, byForegroundService /* byForegroundService */);
 
                         foregroundNoti = localForegroundNoti; // save it for amending next time
 
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
index fc3d7c8114b02fe9049def4c631d4eb391e8b973..745222873698bcbd7283f953499d253feb822bff 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java
@@ -216,6 +216,10 @@ public final class BiometricContextProvider implements BiometricContext {
     public void subscribe(@NonNull OperationContextExt context,
             @NonNull Consumer<OperationContext> consumer) {
         mSubscribers.put(context, consumer);
+        // TODO(b/294161627) Combine the getContext/subscribe APIs to avoid race
+        if (context.getDisplayState() != getDisplayState()) {
+            consumer.accept(context.update(this, context.isCrypto()).toAidlContext());
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 65e34e6827249e7404dec3b6dd0ad90a9877e20c..38aac4648e3474240d645e341e3e476e0ae8e764 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -117,6 +117,10 @@ public final class MediaProjectionManagerService extends SystemService
     // WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService
     // See mediaprojection.md
     private final Object mLock = new Object();
+    // A handler for posting tasks that must interact with a service holding another lock,
+    // especially for services that will eventually acquire the WindowManager lock.
+    @NonNull private final Handler mHandler;
+
     private final Map<IBinder, IBinder.DeathRecipient> mDeathEaters;
     private final CallbackDelegate mCallbackDelegate;
 
@@ -145,6 +149,8 @@ public final class MediaProjectionManagerService extends SystemService
         super(context);
         mContext = context;
         mInjector = injector;
+        // Post messages on the main thread; no need for a separate thread.
+        mHandler = new Handler(Looper.getMainLooper());
         mClock = injector.createClock();
         mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
         mCallbackDelegate = new CallbackDelegate();
@@ -238,15 +244,20 @@ public final class MediaProjectionManagerService extends SystemService
             if (!mProjectionGrant.requiresForegroundService()) {
                 return;
             }
+        }
 
-            if (mActivityManagerInternal.hasRunningForegroundService(
-                    uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
-                // If there is any process within this UID running a FGS
-                // with the mediaProjection type, that's Okay.
-                return;
-            }
+        // Run outside the lock when calling into ActivityManagerService.
+        if (mActivityManagerInternal.hasRunningForegroundService(
+                uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
+            // If there is any process within this UID running a FGS
+            // with the mediaProjection type, that's Okay.
+            return;
+        }
 
-            mProjectionGrant.stop();
+        synchronized (mLock) {
+            if (mProjectionGrant != null) {
+                mProjectionGrant.stop();
+            }
         }
     }
 
@@ -854,7 +865,6 @@ public final class MediaProjectionManagerService extends SystemService
             mTargetSdkVersion = targetSdkVersion;
             mIsPrivileged = isPrivileged;
             mCreateTimeMs = mClock.uptimeMillis();
-            // TODO(b/267740338): Add unit test.
             mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
                     MEDIA_PROJECTION_TOKEN_EVENT_CREATED);
         }
@@ -911,6 +921,10 @@ public final class MediaProjectionManagerService extends SystemService
             if (callback == null) {
                 throw new IllegalArgumentException("callback must not be null");
             }
+            // Cache result of calling into ActivityManagerService outside of the lock, to prevent
+            // deadlock with WindowManagerService.
+            final boolean hasFGS = mActivityManagerInternal.hasRunningForegroundService(
+                    uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
             synchronized (mLock) {
                 if (isCurrentProjection(asBinder())) {
                     Slog.w(TAG, "UID " + Binder.getCallingUid()
@@ -922,9 +936,7 @@ public final class MediaProjectionManagerService extends SystemService
                 }
 
                 if (REQUIRE_FG_SERVICE_FOR_PROJECTION
-                        && requiresForegroundService()
-                        && !mActivityManagerInternal.hasRunningForegroundService(
-                                uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
+                        && requiresForegroundService() && !hasFGS) {
                     throw new SecurityException("Media projections require a foreground service"
                             + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION");
                 }
@@ -1013,10 +1025,11 @@ public final class MediaProjectionManagerService extends SystemService
                 mToken = null;
                 unregisterCallback(mCallback);
                 mCallback = null;
-                // TODO(b/267740338): Add unit test.
-                mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
-                        MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED);
             }
+            // Run on a separate thread, to ensure no lock is held when calling into
+            // ActivityManagerService.
+            mHandler.post(() -> mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(),
+                    MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED));
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/media/projection/mediaprojection.md b/services/core/java/com/android/server/media/projection/mediaprojection.md
index bccdf34119037d34e9e86225a34507b7956cb9d2..34e7ecc6c6c511178cb52639532379434e0a9395 100644
--- a/services/core/java/com/android/server/media/projection/mediaprojection.md
+++ b/services/core/java/com/android/server/media/projection/mediaprojection.md
@@ -11,6 +11,11 @@ Calls must follow the below invocation order while holding locks:
 
 `WindowManagerService -> MediaProjectionManagerService -> DisplayManagerService`
 
+`MediaProjectionManagerService` should never lock when calling into a service that may acquire
+the `WindowManagerService` global lock (for example,
+`MediaProjectionManagerService -> ActivityManagerService` may result in deadlock, since
+`ActivityManagerService -> WindowManagerService`).
+
 ### Justification
 
 `MediaProjectionManagerService` calls into `WindowManagerService` in the below cases. While handling
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 919fc712c4097795d0b1cea900d33772936b2ce6..c240bcbce91190cd31aa768e01e0613151c7806c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -27,6 +27,9 @@ public interface NotificationManagerInternal {
     NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String channelId);
     void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
             String tag, int id, Notification notification, int userId);
+    void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid,
+            String tag, int id, Notification notification, int userId,
+            boolean byForegroundService);
     void cancelNotification(String pkg, String basePkg, int callingUid, int callingPid,
             String tag, int id, int userId);
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7369e5ed59320c5a1a8ce7054d0014a04f94360d..79559fdc34d657fe1a87273a9c0cd3b7f0471bda 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2528,7 +2528,8 @@ public class NotificationManagerService extends SystemService {
                 }
                 enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
                         r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
-                        r.getSbn().getId(),  r.getSbn().getNotification(), userId, muteOnReturn);
+                        r.getSbn().getId(),  r.getSbn().getNotification(), userId, muteOnReturn,
+                        false /* byForegroundService */);
             } catch (Exception e) {
                 Slog.e(TAG, "Cannot un-snooze notification", e);
             }
@@ -3518,7 +3519,8 @@ public class NotificationManagerService extends SystemService {
         public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
                 Notification notification, int userId) throws RemoteException {
             enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
-                    Binder.getCallingPid(), tag, id, notification, userId);
+                    Binder.getCallingPid(), tag, id, notification, userId,
+                    false /* byForegroundService */);
         }
 
         @Override
@@ -6074,7 +6076,7 @@ public class NotificationManagerService extends SystemService {
             }
             if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid,
                     summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
-                    true)) {
+                    true, false)) {
                 return summaryRecord;
             }
         }
@@ -6403,7 +6405,15 @@ public class NotificationManagerService extends SystemService {
         public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
                 String tag, int id, Notification notification, int userId) {
             enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
-                    userId);
+                    userId, false /* byForegroundService */);
+        }
+
+        @Override
+        public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid,
+                String tag, int id, Notification notification, int userId,
+                boolean byForegroundService) {
+            enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
+                    userId, byForegroundService);
         }
 
         @Override
@@ -6581,19 +6591,19 @@ public class NotificationManagerService extends SystemService {
 
     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
             final int callingPid, final String tag, final int id, final Notification notification,
-            int incomingUserId) {
+            int incomingUserId, boolean byForegroundService) {
         enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
-                incomingUserId, false);
+                incomingUserId, false /* postSilently */, byForegroundService);
     }
 
     void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
             final int callingPid, final String tag, final int id, final Notification notification,
-            int incomingUserId, boolean postSilently) {
+            int incomingUserId, boolean postSilently, boolean byForegroundService) {
         PostNotificationTracker tracker = acquireWakeLockForPost(pkg, callingUid);
         boolean enqueued = false;
         try {
             enqueued = enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id,
-                    notification, incomingUserId, postSilently, tracker);
+                    notification, incomingUserId, postSilently, tracker, byForegroundService);
         } finally {
             if (!enqueued) {
                 tracker.cancel();
@@ -6624,10 +6634,10 @@ public class NotificationManagerService extends SystemService {
      * @return True if we successfully processed the notification and handed off the task of
      * enqueueing it to a background thread; false otherwise.
      */
-    private boolean enqueueNotificationInternal(final String pkg, final String opPkg,
+    private boolean enqueueNotificationInternal(final String pkg, final String opPkg,  //HUI
             final int callingUid, final int callingPid, final String tag, final int id,
             final Notification notification, int incomingUserId, boolean postSilently,
-            PostNotificationTracker tracker) {
+            PostNotificationTracker tracker, boolean byForegroundService) {
         if (DBG) {
             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
                     + " notification=" + notification);
@@ -6773,7 +6783,7 @@ public class NotificationManagerService extends SystemService {
                 mPreferencesHelper.hasUserDemotedInvalidMsgApp(pkg, notificationUid));
 
         if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
-                r.getSbn().getOverrideGroupKey() != null)) {
+                r.getSbn().getOverrideGroupKey() != null, byForegroundService)) {
             return false;
         }
 
@@ -7193,7 +7203,7 @@ public class NotificationManagerService extends SystemService {
      * Has side effects.
      */
     boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
-            NotificationRecord r, boolean isAutogroup) {
+            NotificationRecord r, boolean isAutogroup, boolean byForegroundService) {
         Notification n = r.getNotification();
         final String pkg = r.getSbn().getPackageName();
         final boolean isSystemNotification =
@@ -7283,7 +7293,8 @@ public class NotificationManagerService extends SystemService {
         if (n.isStyle(Notification.CallStyle.class)) {
             boolean hasFullScreenIntent = n.fullScreenIntent != null;
             boolean requestedFullScreenIntent = (n.flags & FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
-            if (!n.isFgsOrUij() && !hasFullScreenIntent && !requestedFullScreenIntent) {
+            if (!n.isFgsOrUij() && !hasFullScreenIntent && !requestedFullScreenIntent
+                    && !byForegroundService) {
                 throw new IllegalArgumentException(r.getKey() + " Not posted."
                         + " CallStyle notifications must be for a foreground service or"
                         + " user initated job or use a fullScreenIntent.");
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3f1a077740ccfc6439b3c8adab56e2c78afac346..4c0c4b5add44a8184da0a6e0695844c9e9cd8a6d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -176,7 +176,7 @@ public class DisplayPolicy {
 
     // TODO(b/266197298): Remove this by a more general protocol from the insets providers.
     private static final boolean USE_CACHED_INSETS_FOR_DISPLAY_SWITCH =
-            SystemProperties.getBoolean("persist.wm.debug.cached_insets_switch", false);
+            SystemProperties.getBoolean("persist.wm.debug.cached_insets_switch", true);
 
     private final WindowManagerService mService;
     private final Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
index a4423038a072a65852527272bc49c354c36cb0e9..437510595ecb5787c1667024428aefc81e6f07ca 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java
@@ -251,6 +251,14 @@ public class BiometricContextProviderTest {
         assertThat(actual).containsExactly(true, false, true, true, false, false).inOrder();
     }
 
+    @Test
+    public void testSubscribesWithDifferentState() throws RemoteException {
+        final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
+        mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_AOD);
+        mProvider.subscribe(mOpContext, nonEmptyConsumer);
+        verify(nonEmptyConsumer).accept(same(mOpContext.toAidlContext()));
+    }
+
     @Test
     public void testUnsubscribes() throws RemoteException {
         final Consumer<OperationContext> emptyConsumer = mock(Consumer.class);
@@ -259,6 +267,9 @@ public class BiometricContextProviderTest {
 
         mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_AOD);
 
+        //reset to unknown to avoid trigger accept when subscribe
+        mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_UNKNOWN);
+
         final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class);
         mProvider.subscribe(mOpContext, nonEmptyConsumer);
         mListener.onDisplayStateChanged(AuthenticateOptions.DISPLAY_STATE_LOCKSCREEN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4849665b0c4445dcca57363b0896d39f3b98c82d..72c9791a3ceb25f363292eab87fffd74867070db 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -10104,7 +10104,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
 
         try {
             mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
-                    r.getSbn().getTag(), r,false);
+                    r.getSbn().getTag(), r, false, false);
             fail("Allowed a contextual direct reply with an immutable intent to be posted");
         } catch (IllegalArgumentException e) {
             // good
@@ -10135,7 +10135,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r.applyAdjustments();
 
         mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
-                r.getSbn().getTag(), r,false);
+                r.getSbn().getTag(), r, false, false);
     }
 
     @Test
@@ -10169,7 +10169,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r.applyAdjustments();
 
         mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
-                    r.getSbn().getTag(), r,false);
+                    r.getSbn().getTag(), r, false, false);
     }
 
     @Test
@@ -10382,7 +10382,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
 
         // normal blocked notifications - blocked
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
 
         // just using the style - blocked
         nb.setStyle(new Notification.MediaStyle());
@@ -10391,7 +10391,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
 
         // using the style, but incorrect type in session - blocked
         nb.setStyle(new Notification.MediaStyle());
@@ -10403,7 +10403,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
 
         // style + media session - bypasses block
         nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
@@ -10412,7 +10412,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
     }
 
     @Test
@@ -10495,7 +10495,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
 
         // normal blocked notifications - blocked
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
 
         // just using the style - blocked
         Person person = new Person.Builder()
@@ -10509,36 +10509,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isFalse();
 
         // style + managed call - bypasses block
         when(mTelecomManager.isInManagedCall()).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
 
         // style + self managed call - bypasses block
         when(mTelecomManager.isInSelfManagedCall(
                 r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
 
         // set telecom manager to null - blocked
         mService.setTelecomManager(null);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                           r.getSbn().getId(), r.getSbn().getTag(), r, false))
+                           r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
                 .isFalse();
 
         // set telecom feature to false - blocked
         when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                           r.getSbn().getId(), r.getSbn().getTag(), r, false))
+                           r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
                 .isFalse();
 
         // telecom manager is not ready - blocked
         mService.setTelecomManager(mTelecomManager);
         when(mTelecomManager.isInCall()).thenThrow(new IllegalStateException("not ready"));
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false))
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false))
                 .isFalse();
     }
 
@@ -11058,7 +11058,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
 
         try {
             mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                    r.getSbn().getId(), r.getSbn().getTag(), r, false);
+                    r.getSbn().getId(), r.getSbn().getTag(), r, false, false);
             assertFalse("CallStyle should not be allowed without a valid use case", true);
         } catch (IllegalArgumentException error) {
             assertThat(error.getMessage()).contains("CallStyle");
@@ -11078,7 +11078,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
+    }
+
+    @Test
+    public void checkCallStyleNotification_allowedForByForegroundService() throws Exception {
+        Person person = new Person.Builder().setName("caller").build();
+        Notification n = new Notification.Builder(mContext, "test")
+                // Without FLAG_FOREGROUND_SERVICE.
+                //.setFlag(FLAG_FOREGROUND_SERVICE, true)
+                .setStyle(Notification.CallStyle.forOngoingCall(
+                        person, mock(PendingIntent.class)))
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                n, UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false,
+                true /* byForegroundService */)).isTrue();
     }
 
     @Test
@@ -11094,7 +11112,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
     }
 
     @Test
@@ -11110,7 +11128,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
     }
 
     @Test
@@ -11126,7 +11144,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+                r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
     }
 
     @Test