Merge "Account for runtime component enabled state to check if OEM UI is enabled." into main
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 11da20a..159c17e 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -215,111 +215,37 @@
 
 java_library {
     name: "services.core.ravenwood-jarjar",
+    defaults: ["ravenwood-internal-only-visibility-java"],
     installable: false,
     static_libs: [
         "services.core.ravenwood",
     ],
     jarjar_rules: ":ravenwood-services-jarjar-rules",
-    visibility: ["//visibility:private"],
-}
-
-java_library {
-    name: "services.fakes.ravenwood-jarjar",
-    installable: false,
-    srcs: [":services.fakes-sources"],
-    libs: [
-        "ravenwood-framework",
-        "services.core.ravenwood",
-    ],
-    jarjar_rules: ":ravenwood-services-jarjar-rules",
-    visibility: ["//visibility:private"],
-}
-
-java_library {
-    name: "mockito-ravenwood-prebuilt",
-    installable: false,
-    static_libs: [
-        "mockito-robolectric-prebuilt",
-    ],
-}
-
-java_library {
-    name: "inline-mockito-ravenwood-prebuilt",
-    installable: false,
-    static_libs: [
-        "inline-mockito-robolectric-prebuilt",
-    ],
 }
 
 // Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
 // Rename some of the dependencies to make sure they're included in the intended order.
 java_genrule {
     name: "100-framework-minus-apex.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [":framework-minus-apex.ravenwood"],
     out: ["100-framework-minus-apex.ravenwood.jar"],
-    visibility: ["//visibility:private"],
 }
 
 java_genrule {
     // Use 200 to make sure it comes before the mainline stub ("all-updatable...").
     name: "200-kxml2-android",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [":kxml2-android"],
     out: ["200-kxml2-android.jar"],
-    visibility: ["//visibility:private"],
 }
 
 java_genrule {
     name: "z00-all-updatable-modules-system-stubs",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [":all-updatable-modules-system-stubs"],
     out: ["z00-all-updatable-modules-system-stubs.jar"],
-    visibility: ["//visibility:private"],
-}
-
-android_ravenwood_libgroup {
-    name: "ravenwood-runtime",
-    libs: [
-        "100-framework-minus-apex.ravenwood",
-        "200-kxml2-android",
-
-        "ravenwood-runtime-common-ravenwood",
-
-        "android.test.mock.ravenwood",
-        "ravenwood-helper-runtime",
-        "hoststubgen-helper-runtime.ravenwood",
-        "services.core.ravenwood-jarjar",
-        "services.fakes.ravenwood-jarjar",
-
-        // Provide runtime versions of utils linked in below
-        "junit",
-        "truth",
-        "flag-junit",
-        "ravenwood-framework",
-        "ravenwood-junit-impl",
-        "ravenwood-junit-impl-flag",
-        "mockito-ravenwood-prebuilt",
-        "inline-mockito-ravenwood-prebuilt",
-
-        // It's a stub, so it should be towards the end.
-        "z00-all-updatable-modules-system-stubs",
-    ],
-    jni_libs: [
-        "libandroid_runtime",
-        "libravenwood_runtime",
-    ],
-}
-
-android_ravenwood_libgroup {
-    name: "ravenwood-utils",
-    libs: [
-        "junit",
-        "truth",
-        "flag-junit",
-        "ravenwood-framework",
-        "ravenwood-junit",
-        "mockito-ravenwood-prebuilt",
-        "inline-mockito-ravenwood-prebuilt",
-    ],
 }
diff --git a/core/java/android/util/SequenceUtils.java b/core/java/android/util/SequenceUtils.java
new file mode 100644
index 0000000..f833ce3
--- /dev/null
+++ b/core/java/android/util/SequenceUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Utilities to manage an info change seq id to ensure the update is in sync between client and
+ * system server. This should be used for info that can be updated though multiple IPC channel.
+ *
+ * To use it:
+ * 1. The system server should store the current seq as the source of truth, with initializing to
+ * {@link #getInitSeq}.
+ * 2. Whenever a newer info needs to be sent to the client side, the system server should first
+ * update its seq with {@link #getNextSeq}, then send the new info with the new seq to the client.
+ * 3. On the client side, when receiving a new info, it should only consume it if it is newer than
+ * the last received info seq by checking {@link #isIncomingSeqNewer}.
+ *
+ * @hide
+ */
+public final class SequenceUtils {
+
+    private SequenceUtils() {
+    }
+
+    /**
+     * Returns {@code true} if the incomingSeq is newer than the curSeq.
+     */
+    public static boolean isIncomingSeqNewer(int curSeq, int incomingSeq) {
+        // Convert to long for comparison.
+        final long diff = (long) incomingSeq - curSeq;
+        // If there has been a sufficiently large jump, assume the sequence has wrapped around.
+        // For example, when the last seq is MAX_VALUE, the incoming seq will be MIN_VALUE + 1.
+        // diff = MIN_VALUE + 1 - MAX_VALUE. It is smaller than 0, but should be treated as newer.
+        return diff > 0 || diff < Integer.MIN_VALUE;
+    }
+
+    /** Returns the initial seq. */
+    public static int getInitSeq() {
+        return Integer.MIN_VALUE;
+    }
+
+    /** Returns the next seq. */
+    public static int getNextSeq(int seq) {
+        return seq == Integer.MAX_VALUE
+                // Skip the initial seq, so that when the app process is relaunched, the incoming
+                // seq from the server is always treated as newer.
+                ? getInitSeq() + 1
+                : ++seq;
+    }
+}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 487214c..2efa647 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -18,6 +18,7 @@
 
 import static android.graphics.PointProto.X;
 import static android.graphics.PointProto.Y;
+import static android.util.SequenceUtils.getInitSeq;
 import static android.view.InsetsSourceControlProto.LEASH;
 import static android.view.InsetsSourceControlProto.POSITION;
 import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
@@ -266,6 +267,9 @@
 
         private @Nullable InsetsSourceControl[] mControls;
 
+        /** To make sure the info update between client and system server is in order. */
+        private int mSeq = getInitSeq();
+
         public Array() {
         }
 
@@ -280,9 +284,18 @@
             readFromParcel(in);
         }
 
+        public int getSeq() {
+            return mSeq;
+        }
+
+        public void setSeq(int seq) {
+            mSeq = seq;
+        }
+
         /** Updates the current Array to the given Array. */
         public void setTo(@NonNull Array other, boolean copyControls) {
             set(other.mControls, copyControls);
+            mSeq = other.mSeq;
         }
 
         /** Updates the current controls to the given controls. */
@@ -336,11 +349,13 @@
 
         public void readFromParcel(Parcel in) {
             mControls = in.createTypedArray(InsetsSourceControl.CREATOR);
+            mSeq = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             out.writeTypedArray(mControls, flags);
+            out.writeInt(mSeq);
         }
 
         public static final @NonNull Creator<Array> CREATOR = new Creator<>() {
@@ -362,6 +377,7 @@
                 return false;
             }
             final InsetsSourceControl.Array other = (InsetsSourceControl.Array) o;
+            // mSeq is for internal bookkeeping only.
             return Arrays.equals(mControls, other.mControls);
         }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 21eec67..bbd9acf 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.util.SequenceUtils.getInitSeq;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
 import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
@@ -95,6 +96,9 @@
     /** The display shape */
     private DisplayShape mDisplayShape = DisplayShape.NONE;
 
+    /** To make sure the info update between client and system server is in order. */
+    private int mSeq = getInitSeq();
+
     public InsetsState() {
         mSources = new SparseArray<>();
     }
@@ -586,6 +590,14 @@
         }
     }
 
+    public int getSeq() {
+        return mSeq;
+    }
+
+    public void setSeq(int seq) {
+        mSeq = seq;
+    }
+
     public void set(InsetsState other) {
         set(other, false /* copySources */);
     }
@@ -597,6 +609,7 @@
         mRoundedCornerFrame.set(other.mRoundedCornerFrame);
         mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
         mDisplayShape = other.getDisplayShape();
+        mSeq = other.mSeq;
         mSources.clear();
         for (int i = 0, size = other.mSources.size(); i < size; i++) {
             final InsetsSource otherSource = other.mSources.valueAt(i);
@@ -620,6 +633,7 @@
         mRoundedCornerFrame.set(other.mRoundedCornerFrame);
         mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
         mDisplayShape = other.getDisplayShape();
+        mSeq = other.mSeq;
         if (types == 0) {
             return;
         }
@@ -705,6 +719,7 @@
                 || !mRoundedCornerFrame.equals(state.mRoundedCornerFrame)
                 || !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)
                 || !mDisplayShape.equals(state.mDisplayShape)) {
+            // mSeq is for internal bookkeeping only.
             return false;
         }
 
@@ -778,6 +793,7 @@
         mRoundedCornerFrame.writeToParcel(dest, flags);
         dest.writeTypedObject(mPrivacyIndicatorBounds, flags);
         dest.writeTypedObject(mDisplayShape, flags);
+        dest.writeInt(mSeq);
         final int size = mSources.size();
         dest.writeInt(size);
         for (int i = 0; i < size; i++) {
@@ -803,6 +819,7 @@
         mRoundedCornerFrame.readFromParcel(in);
         mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR);
         mDisplayShape = in.readTypedObject(DisplayShape.CREATOR);
+        mSeq = in.readInt();
         final int size = in.readInt();
         final SparseArray<InsetsSource> sources;
         if (mSources == null) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 405abd7..f653524 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -4508,6 +4508,10 @@
                 Log.w(TAG, "addTransactionCompletedListener was called but flag is disabled");
                 return this;
             }
+            if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+                SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+                        "setFrameTimeline", this, null, "vsyncId=" + vsyncId);
+            }
             nativeSetFrameTimelineVsync(mNativeObject, vsyncId);
             return this;
         }
@@ -4515,6 +4519,11 @@
         /** @hide */
         @NonNull
         public Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
+            if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+                SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+                        "setFrameTimelineVsync", this, null, "frameTimelineVsyncId="
+                                + frameTimelineVsyncId);
+            }
             nativeSetFrameTimelineVsync(mNativeObject, frameTimelineVsyncId);
             return this;
         }
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index d5398e6..781a901 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static android.util.SequenceUtils.getInitSeq;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
@@ -53,6 +55,9 @@
 
     public float compatScale = 1f;
 
+    /** To make sure the info update between client and system server is in order. */
+    public int seq = getInitSeq();
+
     public ClientWindowFrames() {
     }
 
@@ -74,6 +79,7 @@
         }
         isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
         compatScale = other.compatScale;
+        seq = other.seq;
     }
 
     /** Needed for AIDL out parameters. */
@@ -84,6 +90,7 @@
         attachedFrame = in.readTypedObject(Rect.CREATOR);
         isParentFrameClippedByDisplayCutout = in.readBoolean();
         compatScale = in.readFloat();
+        seq = in.readInt();
     }
 
     @Override
@@ -94,6 +101,7 @@
         dest.writeTypedObject(attachedFrame, flags);
         dest.writeBoolean(isParentFrameClippedByDisplayCutout);
         dest.writeFloat(compatScale);
+        dest.writeInt(seq);
     }
 
     @Override
@@ -116,6 +124,7 @@
             return false;
         }
         final ClientWindowFrames other = (ClientWindowFrames) o;
+        // seq is for internal bookkeeping only.
         return frame.equals(other.frame)
                 && displayFrame.equals(other.displayFrame)
                 && parentFrame.equals(other.parentFrame)
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index a7aef92..6b0ca9f 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -181,6 +181,27 @@
     }
 
     /**
+     * Converts {@link Settings.Secure} key to {@link UserShortcutType}.
+     *
+     * @param key The shortcut key in Settings.
+     * @return The mapped type
+     */
+    @UserShortcutType
+    public static int convertToType(String key) {
+        return switch (key) {
+            case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
+            case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
+            case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
+            case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
+                    UserShortcutType.TRIPLETAP;
+            case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
+                    UserShortcutType.TWOFINGER_DOUBLETAP;
+            default -> throw new IllegalArgumentException(
+                    "Unsupported user shortcut key: " + key);
+        };
+    }
+
+    /**
      * Updates an accessibility state if the accessibility service is a Always-On a11y service,
      * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON
      * <p>
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index e21d1df..911bb19 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -111,6 +111,7 @@
         "libminikin",
         "libz",
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc",
         "android.database.sqlite-aconfig-cc",
         "android.media.audiopolicy-aconfig-cc",
     ],
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 851e612..8cd6773 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.MediumTest;
@@ -154,7 +153,6 @@
 
     @Test
     @MediumTest
-    @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
     public void testFieldIntegrity() throws Exception {
 
         TestHandlerThread tester = new TestFieldIntegrityHandler() {
diff --git a/core/tests/coretests/src/android/util/SequenceUtilsTest.java b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
new file mode 100644
index 0000000..020520d
--- /dev/null
+++ b/core/tests/coretests/src/android/util/SequenceUtilsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+
+import static android.util.SequenceUtils.getInitSeq;
+import static android.util.SequenceUtils.getNextSeq;
+import static android.util.SequenceUtils.isIncomingSeqNewer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for subtypes of {@link SequenceUtils}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:SequenceUtilsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+@DisabledOnRavenwood(blockedBy = SequenceUtils.class)
+public class SequenceUtilsTest {
+
+    // This is needed to disable the test in Ravenwood test, because SequenceUtils hasn't opted in
+    // for Ravenwood, which is still in experiment.
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Test
+    public void testNextSeq() {
+        assertEquals(getInitSeq() + 1, getNextSeq(getInitSeq()));
+        assertEquals(getInitSeq() + 1, getNextSeq(Integer.MAX_VALUE));
+    }
+
+    @Test
+    public void testIsIncomingSeqNewer() {
+        assertTrue(isIncomingSeqNewer(getInitSeq() + 1, getInitSeq() + 10));
+        assertFalse(isIncomingSeqNewer(getInitSeq() + 10, getInitSeq() + 1));
+        assertTrue(isIncomingSeqNewer(-100, 100));
+        assertFalse(isIncomingSeqNewer(100, -100));
+        assertTrue(isIncomingSeqNewer(1, 2));
+        assertFalse(isIncomingSeqNewer(2, 1));
+
+        // Possible incoming seq are all newer than the initial seq.
+        assertTrue(isIncomingSeqNewer(getInitSeq(), getInitSeq() + 1));
+        assertTrue(isIncomingSeqNewer(getInitSeq(), -100));
+        assertTrue(isIncomingSeqNewer(getInitSeq(), 0));
+        assertTrue(isIncomingSeqNewer(getInitSeq(), 100));
+        assertTrue(isIncomingSeqNewer(getInitSeq(), Integer.MAX_VALUE));
+        assertTrue(isIncomingSeqNewer(getInitSeq(), getNextSeq(Integer.MAX_VALUE)));
+
+        // False for the same seq.
+        assertFalse(isIncomingSeqNewer(getInitSeq(), getInitSeq()));
+        assertFalse(isIncomingSeqNewer(100, 100));
+        assertFalse(isIncomingSeqNewer(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+        // True when there is a large jump (overflow).
+        assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 1));
+        assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getInitSeq() + 100));
+        assertTrue(isIncomingSeqNewer(Integer.MAX_VALUE, getNextSeq(Integer.MAX_VALUE)));
+    }
+}
diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
index 1c72185..a8dce70 100644
--- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.filters.SmallTest;
@@ -37,7 +36,6 @@
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
     public void testAddAll() {
         final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
 
@@ -59,7 +57,6 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "b/315036461")
     public void testCopyConstructor() {
         final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>();
 
diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
index ac659e1..6c6feaf 100644
--- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java
@@ -18,17 +18,19 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.TimeZone;
 import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
@@ -42,6 +44,22 @@
     public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
     public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
 
+    private TimeZone mOrigTimezone;
+
+    @Before
+    public void setUp() {
+        mOrigTimezone = TimeZone.getDefault();
+
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+    }
+
+    @After
+    public void tearDown() {
+        if (mOrigTimezone != null) {
+            TimeZone.setDefault(mOrigTimezone);
+        }
+    }
+
     @Test
     public void testFormatTime() {
         assertEquals("1672556400000 (now)",
@@ -85,32 +103,29 @@
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
     public void testDumpTime() {
-        assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> {
+        assertEquals("2023-01-01 07:00:00.000", runWithPrintWriter((pw) -> {
             TimeUtils.dumpTime(pw, 1672556400000L);
         }));
-        assertEquals("2023-01-01 00:00:00.000 (now)", runWithPrintWriter((pw) -> {
+        assertEquals("2023-01-01 07:00:00.000 (now)", runWithPrintWriter((pw) -> {
             TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L);
         }));
-        assertEquals("2023-01-01 00:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
+        assertEquals("2023-01-01 07:00:00.000 (-10ms)", runWithPrintWriter((pw) -> {
             TimeUtils.dumpTimeWithDelta(pw, 1672556400000L, 1672556400000L + 10);
         }));
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
     public void testFormatForLogging() {
         assertEquals("unknown", TimeUtils.formatForLogging(0));
         assertEquals("unknown", TimeUtils.formatForLogging(-1));
         assertEquals("unknown", TimeUtils.formatForLogging(Long.MIN_VALUE));
-        assertEquals("2023-01-01 00:00:00", TimeUtils.formatForLogging(1672556400000L));
+        assertEquals("2023-01-01 07:00:00", TimeUtils.formatForLogging(1672556400000L));
     }
 
     @Test
-    @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700")
     public void testLogTimeOfDay() {
-        assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
+        assertEquals("01-01 07:00:00.000", TimeUtils.logTimeOfDay(1672556400000L));
     }
 
     public static String runWithPrintWriter(Consumer<PrintWriter> consumer) {
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 3256f31..5caedba 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -416,7 +416,8 @@
     }
 
     /**
-     * Retrieve the dataspace associated with the texture image.
+     * Retrieve the dataspace associated with the texture image
+     * set by the most recent call to {@link #updateTexImage}.
      */
     @SuppressLint("MethodNameUnits")
     public @NamedDataSpace int getDataSpace() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 1279fc4..2aefc64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -894,11 +894,22 @@
     }
 
     @Nullable
-    Intent getAppBubbleIntent() {
+    @VisibleForTesting
+    public Intent getAppBubbleIntent() {
         return mAppIntent;
     }
 
     /**
+     * Sets the intent for a bubble that is an app bubble (one for which {@link #mIsAppBubble} is
+     * true).
+     *
+     * @param appIntent The intent to set for the app bubble.
+     */
+    void setAppBubbleIntent(Intent appIntent) {
+        mAppIntent = appIntent;
+    }
+
+    /**
      * Returns whether this bubble is from an app versus a notification.
      */
     public boolean isAppBubble() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index d2c36e6..c853301 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1450,6 +1450,8 @@
             if (b != null) {
                 // It's in the overflow, so remove it & reinflate
                 mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_NOTIF_CANCEL);
+                // Update the bubble entry in the overflow with the latest intent.
+                b.setAppBubbleIntent(intent);
             } else {
                 // App bubble does not exist, lets add and expand it
                 b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp
index baafe7b..a94c8c5 100644
--- a/packages/CtsShim/Android.bp
+++ b/packages/CtsShim/Android.bp
@@ -61,7 +61,6 @@
         "com.android.apex.cts.shim.v1",
         "com.android.apex.cts.shim.v2",
         "com.android.apex.cts.shim.v2_legacy",
-        "com.android.apex.cts.shim.v2_no_hashtree",
         "com.android.apex.cts.shim.v2_sdk_target_p",
         "com.android.apex.cts.shim.v3",
     ],
@@ -102,7 +101,6 @@
         "com.android.apex.cts.shim.v1",
         "com.android.apex.cts.shim.v2",
         "com.android.apex.cts.shim.v2_legacy",
-        "com.android.apex.cts.shim.v2_no_hashtree",
         "com.android.apex.cts.shim.v2_sdk_target_p",
         "com.android.apex.cts.shim.v3",
     ],
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index d6b7ecf..5b3d47e 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -93,7 +93,6 @@
         "com.android.apex.cts.shim.v1",
         "com.android.apex.cts.shim.v2",
         "com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
-        "com.android.apex.cts.shim.v2_no_hashtree",
         "com.android.apex.cts.shim.v2_legacy",
         "com.android.apex.cts.shim.v2_sdk_target_p",
         "com.android.apex.cts.shim.v2_unsigned_payload",
@@ -200,7 +199,6 @@
         "com.android.apex.cts.shim.v1",
         "com.android.apex.cts.shim.v2",
         "com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
-        "com.android.apex.cts.shim.v2_no_hashtree",
         "com.android.apex.cts.shim.v2_legacy",
         "com.android.apex.cts.shim.v2_sdk_target_p",
         "com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index 2dbf2a2..ac85d2b 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -33,7 +33,6 @@
         "com.android.apex.cts.shim.v1",
         "com.android.apex.cts.shim.v2",
         "com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
-        "com.android.apex.cts.shim.v2_no_hashtree",
         "com.android.apex.cts.shim.v2_legacy",
         "com.android.apex.cts.shim.v2_sdk_target_p",
         "com.android.apex.cts.shim.v2_unsigned_payload",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index ec7150b..5242fe3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.Idle
@@ -450,4 +451,16 @@
             progress.value = 0.9f
             assertThat(transitionValue).isEqualTo(0f)
         }
+
+    @Test
+    fun changeScene_toGone_whenKeyguardDisabled_doesNotThrow() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+            underTest.changeScene(Scenes.Gone, "")
+
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 8dede01..9cc0b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -25,6 +25,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
@@ -48,6 +49,37 @@
     transitionInteractor: KeyguardTransitionInteractor,
 ) {
 
+    /**
+     * Whether the keyguard is enabled, per [KeyguardService]. If the keyguard is not enabled, the
+     * lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE.
+     *
+     * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold
+     * permission to do so (such as Phone).
+     *
+     * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in
+     * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were
+     * locked when it was disabled.
+     */
+    val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled
+
+    /**
+     * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
+     * became disabled.
+     */
+    val showKeyguardWhenReenabled: Flow<Boolean> =
+        repository.isKeyguardEnabled
+            // Whenever the keyguard is disabled...
+            .filter { enabled -> !enabled }
+            .sampleCombine(
+                transitionInteractor.currentTransitionInfoInternal,
+                biometricSettingsRepository.isCurrentUserInLockdown
+            )
+            .map { (_, transitionInfo, inLockdown) ->
+                // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
+                // we want to remember that and re-show it when keyguard is enabled again.
+                transitionInfo.to != KeyguardState.GONE && !inLockdown
+            }
+
     init {
         /**
          * Whenever keyguard is disabled, transition to GONE unless we're in lockdown or already
@@ -68,24 +100,6 @@
         }
     }
 
-    /**
-     * Whether we need to show the keyguard when the keyguard is re-enabled, since we hid it when it
-     * became disabled.
-     */
-    val showKeyguardWhenReenabled: Flow<Boolean> =
-        repository.isKeyguardEnabled
-            // Whenever the keyguard is disabled...
-            .filter { enabled -> !enabled }
-            .sampleCombine(
-                transitionInteractor.currentTransitionInfoInternal,
-                biometricSettingsRepository.isCurrentUserInLockdown
-            )
-            .map { (_, transitionInfo, inLockdown) ->
-                // ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
-                // we want to remember that and re-show it when keyguard is enabled again.
-                transitionInfo.to != KeyguardState.GONE && !inLockdown
-            }
-
     fun notifyKeyguardEnabled(enabled: Boolean) {
         repository.setKeyguardEnabled(enabled)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 4738dbd..25a9e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
 import com.android.systemui.scene.data.repository.SceneContainerRepository
 import com.android.systemui.scene.domain.resolver.SceneResolver
 import com.android.systemui.scene.shared.logger.SceneLogger
@@ -60,6 +61,7 @@
     private val logger: SceneLogger,
     private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
+    private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
 ) {
 
     interface OnSceneAboutToChangeListener {
@@ -381,7 +383,8 @@
         val isChangeAllowed =
             to != Scenes.Gone ||
                 inMidTransitionFromGone ||
-                deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked
+                deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked ||
+                !keyguardEnabledInteractor.isKeyguardEnabled.value
         check(isChangeAllowed) {
             "Cannot change to the Gone scene while the device is locked and not currently" +
                 " transitioning from Gone. Current transition state is ${transitionState.value}." +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 4c66f66..0bb18d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -58,7 +58,7 @@
 constructor(
     @Application applicationScope: CoroutineScope,
     dumpManager: DumpManager,
-    private val mHeadsUpManager: HeadsUpManager,
+    private val headsUpManager: HeadsUpManager,
     private val statusBarStateController: StatusBarStateController,
     private val bypassController: KeyguardBypassController,
     private val dozeParameters: DozeParameters,
@@ -71,8 +71,8 @@
     StatusBarStateController.StateListener,
     ShadeExpansionListener,
     Dumpable {
-    private lateinit var mStackScrollerController: NotificationStackScrollLayoutController
-    private var mVisibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+    private lateinit var stackScrollerController: NotificationStackScrollLayoutController
+    private var visibilityInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE
 
     private var inputLinearDozeAmount: Float = 0.0f
     private var inputEasedDozeAmount: Float = 0.0f
@@ -85,13 +85,13 @@
     private var outputEasedDozeAmount: Float = 0.0f
     @VisibleForTesting val dozeAmountInterpolator: Interpolator = Interpolators.FAST_OUT_SLOW_IN
 
-    private var mNotificationVisibleAmount = 0.0f
-    private var mNotificationsVisible = false
-    private var mNotificationsVisibleForExpansion = false
-    private var mVisibilityAnimator: ObjectAnimator? = null
-    private var mVisibilityAmount = 0.0f
-    private var mLinearVisibilityAmount = 0.0f
-    private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
+    private var notificationVisibleAmount = 0.0f
+    private var notificationsVisible = false
+    private var notificationsVisibleForExpansion = false
+    private var visibilityAnimator: ObjectAnimator? = null
+    private var visibilityAmount = 0.0f
+    private var linearVisibilityAmount = 0.0f
+    private val entrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
     private var pulseExpanding: Boolean = false
     private val wakeUpListeners = arrayListOf<WakeUpListener>()
     private var state: Int = StatusBarState.KEYGUARD
@@ -104,14 +104,14 @@
             willWakeUp = false
             if (value) {
                 if (
-                    mNotificationsVisible &&
-                        !mNotificationsVisibleForExpansion &&
+                    notificationsVisible &&
+                        !notificationsVisibleForExpansion &&
                         !bypassController.bypassEnabled
                 ) {
                     // We're waking up while pulsing, let's make sure the animation looks nice
-                    mStackScrollerController.wakeUpFromPulse()
+                    stackScrollerController.wakeUpFromPulse()
                 }
-                if (bypassController.bypassEnabled && !mNotificationsVisible) {
+                if (bypassController.bypassEnabled && !notificationsVisible) {
                     // Let's make sure our huns become visible once we are waking up in case
                     // they were blocked by the proximity sensor
                     updateNotificationVisibility(
@@ -186,13 +186,13 @@
 
     init {
         dumpManager.registerDumpable(this)
-        mHeadsUpManager.addListener(this)
+        headsUpManager.addListener(this)
         statusBarStateController.addCallback(this)
         bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
         addListener(
             object : WakeUpListener {
                 override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
-                    if (isFullyHidden && mNotificationsVisibleForExpansion) {
+                    if (isFullyHidden && notificationsVisibleForExpansion) {
                         // When the notification becomes fully invisible, let's make sure our
                         // expansion
                         // flag also changes. This can happen if the bouncer shows when dragging
@@ -217,7 +217,7 @@
     }
 
     fun setStackScroller(stackScrollerController: NotificationStackScrollLayoutController) {
-        mStackScrollerController = stackScrollerController
+        this.stackScrollerController = stackScrollerController
         pulseExpanding = stackScrollerController.isPulseExpanding
         stackScrollerController.setOnPulseHeightChangedListener {
             val nowExpanding = isPulseExpanding()
@@ -237,7 +237,7 @@
         }
     }
 
-    fun isPulseExpanding(): Boolean = mStackScrollerController.isPulseExpanding
+    fun isPulseExpanding(): Boolean = stackScrollerController.isPulseExpanding
 
     /**
      * @param visible should notifications be visible
@@ -249,13 +249,13 @@
         animate: Boolean,
         increaseSpeed: Boolean
     ) {
-        mNotificationsVisibleForExpansion = visible
+        notificationsVisibleForExpansion = visible
         updateNotificationVisibility(animate, increaseSpeed)
-        if (!visible && mNotificationsVisible) {
+        if (!visible && notificationsVisible) {
             // If we stopped expanding and we're still visible because we had a pulse that hasn't
             // times out, let's release them all to make sure were not stuck in a state where
             // notifications are visible
-            mHeadsUpManager.releaseAllImmediately()
+            headsUpManager.releaseAllImmediately()
         }
     }
 
@@ -269,12 +269,12 @@
 
     private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
         // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
-        var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
+        var visible = notificationsVisibleForExpansion || headsUpManager.hasNotifications()
         visible = visible && canShowPulsingHuns
 
         if (
             !visible &&
-                mNotificationsVisible &&
+                notificationsVisible &&
                 (wakingUp || willWakeUp) &&
                 outputLinearDozeAmount != 0.0f
         ) {
@@ -290,11 +290,11 @@
         animate: Boolean,
         increaseSpeed: Boolean
     ) {
-        if (mNotificationsVisible == visible) {
+        if (notificationsVisible == visible) {
             return
         }
-        mNotificationsVisible = visible
-        mVisibilityAnimator?.cancel()
+        notificationsVisible = visible
+        visibilityAnimator?.cancel()
         if (animate) {
             notifyAnimationStart(visible)
             startVisibilityAnimation(increaseSpeed)
@@ -371,7 +371,7 @@
             state = statusBarStateController.state,
             changed = changed
         )
-        mStackScrollerController.setDozeAmount(outputEasedDozeAmount)
+        stackScrollerController.setDozeAmount(outputEasedDozeAmount)
         updateHideAmount()
         if (changed && outputLinearDozeAmount == 0.0f) {
             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
@@ -475,7 +475,7 @@
             this.collapsedEnoughToHide = collapsedEnough
             if (couldShowPulsingHuns && !canShowPulsingHuns) {
                 updateNotificationVisibility(animate = true, increaseSpeed = true)
-                mHeadsUpManager.releaseAllImmediately()
+                headsUpManager.releaseAllImmediately()
             }
         }
     }
@@ -562,12 +562,12 @@
     }
 
     private fun startVisibilityAnimation(increaseSpeed: Boolean) {
-        if (mNotificationVisibleAmount == 0f || mNotificationVisibleAmount == 1f) {
-            mVisibilityInterpolator =
-                if (mNotificationsVisible) Interpolators.TOUCH_RESPONSE
+        if (notificationVisibleAmount == 0f || notificationVisibleAmount == 1f) {
+            visibilityInterpolator =
+                if (notificationsVisible) Interpolators.TOUCH_RESPONSE
                 else Interpolators.FAST_OUT_SLOW_IN_REVERSE
         }
-        val target = if (mNotificationsVisible) 1.0f else 0.0f
+        val target = if (notificationsVisible) 1.0f else 0.0f
         val visibilityAnimator = ObjectAnimator.ofFloat(this, notificationVisibility, target)
         visibilityAnimator.interpolator = InterpolatorsAndroidX.LINEAR
         var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
@@ -576,34 +576,34 @@
         }
         visibilityAnimator.duration = duration
         visibilityAnimator.start()
-        mVisibilityAnimator = visibilityAnimator
+        this.visibilityAnimator = visibilityAnimator
     }
 
     private fun setVisibilityAmount(visibilityAmount: Float) {
         logger.logSetVisibilityAmount(visibilityAmount)
-        mLinearVisibilityAmount = visibilityAmount
-        mVisibilityAmount = mVisibilityInterpolator.getInterpolation(visibilityAmount)
+        linearVisibilityAmount = visibilityAmount
+        this.visibilityAmount = visibilityInterpolator.getInterpolation(visibilityAmount)
         handleAnimationFinished()
         updateHideAmount()
     }
 
     private fun handleAnimationFinished() {
-        if (outputLinearDozeAmount == 0.0f || mLinearVisibilityAmount == 0.0f) {
-            mEntrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
-            mEntrySetToClearWhenFinished.clear()
+        if (outputLinearDozeAmount == 0.0f || linearVisibilityAmount == 0.0f) {
+            entrySetToClearWhenFinished.forEach { it.setHeadsUpAnimatingAway(false) }
+            entrySetToClearWhenFinished.clear()
         }
     }
 
     private fun updateHideAmount() {
-        val linearAmount = min(1.0f - mLinearVisibilityAmount, outputLinearDozeAmount)
-        val amount = min(1.0f - mVisibilityAmount, outputEasedDozeAmount)
+        val linearAmount = min(1.0f - linearVisibilityAmount, outputLinearDozeAmount)
+        val amount = min(1.0f - visibilityAmount, outputEasedDozeAmount)
         logger.logSetHideAmount(linearAmount)
-        mStackScrollerController.setHideAmount(linearAmount, amount)
+        stackScrollerController.setHideAmount(linearAmount, amount)
         notificationsFullyHidden = linearAmount == 1.0f
     }
 
     private fun notifyAnimationStart(awake: Boolean) {
-        mStackScrollerController.notifyHideAnimationStart(!awake)
+        stackScrollerController.notifyHideAnimationStart(!awake)
     }
 
     override fun onDozingChanged(isDozing: Boolean) {
@@ -615,7 +615,7 @@
     override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
         var animate = shouldAnimateVisibility()
         if (!isHeadsUp) {
-            if (outputLinearDozeAmount != 0.0f && mLinearVisibilityAmount != 0.0f) {
+            if (outputLinearDozeAmount != 0.0f && linearVisibilityAmount != 0.0f) {
                 if (entry.isRowDismissed) {
                     // if we animate, we see the shelf briefly visible. Instead we fully animate
                     // the notification and its background out
@@ -623,11 +623,11 @@
                 } else if (!wakingUp && !willWakeUp) {
                     // TODO: look that this is done properly and not by anyone else
                     entry.setHeadsUpAnimatingAway(true)
-                    mEntrySetToClearWhenFinished.add(entry)
+                    entrySetToClearWhenFinished.add(entry)
                 }
             }
-        } else if (mEntrySetToClearWhenFinished.contains(entry)) {
-            mEntrySetToClearWhenFinished.remove(entry)
+        } else if (entrySetToClearWhenFinished.contains(entry)) {
+            entrySetToClearWhenFinished.remove(entry)
             entry.setHeadsUpAnimatingAway(false)
         }
         updateNotificationVisibility(animate, increaseSpeed = false)
@@ -644,11 +644,11 @@
         pw.println("hardDozeAmountOverrideSource: $hardDozeAmountOverrideSource")
         pw.println("outputLinearDozeAmount: $outputLinearDozeAmount")
         pw.println("outputEasedDozeAmount: $outputEasedDozeAmount")
-        pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
-        pw.println("mNotificationsVisible: $mNotificationsVisible")
-        pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
-        pw.println("mVisibilityAmount: $mVisibilityAmount")
-        pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+        pw.println("notificationVisibleAmount: $notificationVisibleAmount")
+        pw.println("notificationsVisible: $notificationsVisible")
+        pw.println("notificationsVisibleForExpansion: $notificationsVisibleForExpansion")
+        pw.println("visibilityAmount: $visibilityAmount")
+        pw.println("linearVisibilityAmount: $linearVisibilityAmount")
         pw.println("pulseExpanding: $pulseExpanding")
         pw.println("state: ${StatusBarState.toString(state)}")
         pw.println("fullyAwake: $fullyAwake")
@@ -698,7 +698,7 @@
                 }
 
                 override fun get(coordinator: NotificationWakeUpCoordinator): Float {
-                    return coordinator.mLinearVisibilityAmount
+                    return coordinator.linearVisibilityAmount
                 }
             }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fabb9b7..c5fbc39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -25,6 +25,8 @@
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
+import static androidx.test.ext.truth.content.IntentSubject.assertThat;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
 import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
@@ -2017,6 +2019,31 @@
     }
 
     @Test
+    public void testShowOrHideAppBubble_updateExistedBubbleInOverflow_updateIntentInBubble() {
+        String appBubbleKey = Bubble.getAppBubbleKeyForApp(mAppBubbleIntent.getPackage(), mUser0);
+        mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+        // Collapse the stack so we don't need to wait for the dismiss animation in the test
+        mBubbleController.collapseStack();
+        // Dismiss the app bubble so it's in the overflow
+        mBubbleController.dismissBubble(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNotNull();
+
+        // Modify the intent to include new extras.
+        Intent newAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class)
+                .setPackage(mContext.getPackageName())
+                .putExtra("hello", "world");
+
+        // Calling this while collapsed will re-add and expand the app bubble
+        mBubbleController.showOrHideAppBubble(newAppBubbleIntent, mUser0, mAppBubbleIcon);
+        assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
+        assertThat(mBubbleController.isStackExpanded()).isTrue();
+        assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
+        assertThat(mBubbleData.getBubbles().get(0).getAppBubbleIntent()).extras().string(
+                "hello").isEqualTo("world");
+        assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+    }
+
+    @Test
     public void testCreateBubbleFromOngoingNotification() {
         NotificationEntry notif = new NotificationEntryBuilder()
                 .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index 066736c..0921eb9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.scene.domain.interactor
 
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -31,5 +32,6 @@
             logger = sceneLogger,
             sceneFamilyResolvers = { sceneFamilyResolvers },
             deviceUnlockedInteractor = deviceUnlockedInteractor,
+            keyguardEnabledInteractor = keyguardEnabledInteractor,
         )
     }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 48bc803..ad216b5 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -276,3 +276,77 @@
         ":services.core.ravenwood.keep_all",
     ],
 }
+
+java_library {
+    name: "services.fakes.ravenwood-jarjar",
+    installable: false,
+    srcs: [":services.fakes-sources"],
+    libs: [
+        "ravenwood-framework",
+        "services.core.ravenwood",
+    ],
+    jarjar_rules: ":ravenwood-services-jarjar-rules",
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "mockito-ravenwood-prebuilt",
+    installable: false,
+    static_libs: [
+        "mockito-robolectric-prebuilt",
+    ],
+}
+
+java_library {
+    name: "inline-mockito-ravenwood-prebuilt",
+    installable: false,
+    static_libs: [
+        "inline-mockito-robolectric-prebuilt",
+    ],
+}
+
+android_ravenwood_libgroup {
+    name: "ravenwood-runtime",
+    libs: [
+        "100-framework-minus-apex.ravenwood",
+        "200-kxml2-android",
+
+        "ravenwood-runtime-common-ravenwood",
+
+        "android.test.mock.ravenwood",
+        "ravenwood-helper-runtime",
+        "hoststubgen-helper-runtime.ravenwood",
+        "services.core.ravenwood-jarjar",
+        "services.fakes.ravenwood-jarjar",
+
+        // Provide runtime versions of utils linked in below
+        "junit",
+        "truth",
+        "flag-junit",
+        "ravenwood-framework",
+        "ravenwood-junit-impl",
+        "ravenwood-junit-impl-flag",
+        "mockito-ravenwood-prebuilt",
+        "inline-mockito-ravenwood-prebuilt",
+
+        // It's a stub, so it should be towards the end.
+        "z00-all-updatable-modules-system-stubs",
+    ],
+    jni_libs: [
+        "libandroid_runtime",
+        "libravenwood_runtime",
+    ],
+}
+
+android_ravenwood_libgroup {
+    name: "ravenwood-utils",
+    libs: [
+        "junit",
+        "truth",
+        "flag-junit",
+        "ravenwood-framework",
+        "ravenwood-junit",
+        "mockito-ravenwood-prebuilt",
+        "inline-mockito-ravenwood-prebuilt",
+    ],
+}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 9353150..b4efae3 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -11,6 +11,16 @@
 }
 
 flag {
+    name: "always_allow_observing_touch_events"
+    namespace: "accessibility"
+    description: "Always allows InputFilter observing SOURCE_TOUCHSCREEN events, even if touch exploration is enabled."
+    bug: "344604959"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "resettable_dynamic_properties"
     namespace: "accessibility"
     description: "Maintains initial copies of a11yServiceInfo dynamic properties so they can reset on disconnect."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5fb60e7..f9196f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -1087,21 +1087,15 @@
         }
     }
 
-    private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
-        // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
-        // touch exploration.
-        if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
-                && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
-            return false;
-        }
-        final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
-        return (mCombinedGenericMotionEventSources
-                        & mCombinedMotionEventObservedSources
-                        & eventSourceWithoutClass)
-                != 0;
-    }
-
     private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
+        if (Flags.alwaysAllowObservingTouchEvents()) {
+            final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+            if (isTouchEvent && !canShareGenericTouchEvent()) {
+                return false;
+            }
+            final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+            return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
+        }
         // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
         // touch exploration.
         if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
@@ -1112,6 +1106,36 @@
         return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
     }
 
+    private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) {
+        if (Flags.alwaysAllowObservingTouchEvents()) {
+            final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+            return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0;
+        }
+        // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
+        // touch exploration.
+        if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+                && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+            return false;
+        }
+        final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
+        return (mCombinedGenericMotionEventSources
+                & mCombinedMotionEventObservedSources
+                & eventSourceWithoutClass)
+                != 0;
+    }
+
+    private boolean canShareGenericTouchEvent() {
+        if ((mCombinedMotionEventObservedSources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+            // Share touch events if a MotionEvent-observing service wants them.
+            return true;
+        }
+        if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) == 0) {
+            // Share touch events if touch exploration is not enabled.
+            return true;
+        }
+        return false;
+    }
+
     public void setCombinedGenericMotionEventSources(int sources) {
         mCombinedGenericMotionEventSources = sources;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4f9db8b..acd80ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -47,6 +47,11 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
 import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -923,25 +928,11 @@
                                         newValue, restoredFromSdk);
                             }
                         }
-                        case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> {
-                            synchronized (mLock) {
-                                restoreAccessibilityButtonTargetsLocked(
-                                        previousValue, newValue);
-                            }
-                        }
-                        case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> {
-                            if (!android.view.accessibility.Flags.a11yQsShortcut()) {
-                                return;
-                            }
-                            restoreAccessibilityQsTargets(newValue);
-                        }
-                        case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> {
-                            if (!android.view.accessibility.Flags
-                                    .restoreA11yShortcutTargetService()) {
-                                return;
-                            }
-                            restoreAccessibilityShortcutTargetService(previousValue, newValue);
-                        }
+                        case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                                Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+                                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
+                                restoreShortcutTargets(newValue,
+                                        ShortcutUtils.convertToType(which));
                     }
                 }
             }
@@ -1040,7 +1031,7 @@
         }
         persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 userState.mUserId, targetsFromSetting, str -> str);
-        readAccessibilityButtonTargetsLocked(userState);
+        readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
         onUserStateChangedLocked(userState);
     }
 
@@ -1720,12 +1711,12 @@
         }
         // Turn on/off a11y qs shortcut for the a11y features based on the change in QS Panel
         if (!a11yFeaturesToEnable.isEmpty()) {
-            enableShortcutForTargets(/* enable= */ true, UserShortcutType.QUICK_SETTINGS,
+            enableShortcutForTargets(/* enable= */ true, QUICK_SETTINGS,
                     a11yFeaturesToEnable, userId);
         }
 
         if (!a11yFeaturesToRemove.isEmpty()) {
-            enableShortcutForTargets(/* enable= */ false, UserShortcutType.QUICK_SETTINGS,
+            enableShortcutForTargets(/* enable= */ false, QUICK_SETTINGS,
                     a11yFeaturesToRemove, userId);
         }
     }
@@ -2057,100 +2048,78 @@
     }
 
     /**
-     * User could enable accessibility services and configure accessibility button during the SUW.
-     * Merges current value of accessibility button settings into the restored one to make sure
-     * user's preferences of accessibility button updated in SUW are not lost.
-     *
-     * Called only during settings restore; currently supports only the owner user
-     * TODO: http://b/22388012
-     */
-    void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) {
-        final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting,
-                /* doMerge = */false);
-        readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting,
-                /* doMerge = */true);
-
-        final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
-        userState.mAccessibilityButtonTargets.clear();
-        userState.mAccessibilityButtonTargets.addAll(targetsFromSetting);
-        persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str);
-
-        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-        onUserStateChangedLocked(userState);
-    }
-
-    /**
      * User could configure accessibility shortcut during the SUW before restoring user data.
      * Merges the current value and the new value to make sure we don't lost the setting the user's
-     * preferences of accessibility qs shortcut updated in SUW are not lost.
-     *
-     * Called only during settings restore; currently supports only the owner user
+     * preferences of accessibility shortcut updated in SUW are not lost.
+     * Called only during settings restore; currently supports only the owner user.
+     * <P>
+     * Throws an exception if used with {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}.
+     * </P>
      * TODO: http://b/22388012
      */
-    private void restoreAccessibilityQsTargets(String newValue) {
+    private void restoreShortcutTargets(String newValue,
+            @UserShortcutType int shortcutType) {
+        assertNoTapShortcut(shortcutType);
+        if (shortcutType == QUICK_SETTINGS && !android.view.accessibility.Flags.a11yQsShortcut()) {
+            return;
+        }
+        if (shortcutType == HARDWARE
+                && !android.view.accessibility.Flags.restoreA11yShortcutTargetService()) {
+            return;
+        }
+
         synchronized (mLock) {
             final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
-            final Set<String> mergedTargets = userState.getA11yQsTargets();
-            readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
-                    /* doMerge = */ true);
+            final Set<String> mergedTargets = (shortcutType == HARDWARE)
+                    ? new ArraySet<>(ShortcutUtils.getShortcutTargetsFromSettings(
+                            mContext, shortcutType, userState.mUserId))
+                    : userState.getShortcutTargetsLocked(shortcutType);
 
-            userState.updateA11yQsTargetLocked(mergedTargets);
-            persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+            // If dealing with the hardware shortcut,
+            // remove the default service if it wasn't present before restore,
+            // but only if the raw shortcut setting is not null (edge case during SUW).
+            // Otherwise, merge the old and new targets normally.
+            if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()
+                    && shortcutType == HARDWARE) {
+                final String defaultService =
+                        mContext.getString(R.string.config_defaultAccessibilityService);
+                final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
+                        ? null : ComponentName.unflattenFromString(defaultService);
+                boolean shouldClearDefaultService = defaultServiceComponent != null
+                        && !stringSetContainsComponentName(mergedTargets, defaultServiceComponent);
+                readColonDelimitedStringToSet(newValue, str -> str,
+                        mergedTargets, /*doMerge=*/true);
+
+                if (shouldClearDefaultService && stringSetContainsComponentName(
+                        mergedTargets, defaultServiceComponent)) {
+                    Slog.i(LOG_TAG, "Removing default service " + defaultService
+                            + " from restore of "
+                            + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+                    mergedTargets.removeIf(str ->
+                            defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
+                }
+                if (mergedTargets.isEmpty()) {
+                    return;
+                }
+            } else {
+                readColonDelimitedStringToSet(newValue, str -> str, mergedTargets,
+                        /* doMerge = */ true);
+            }
+
+            userState.updateShortcutTargetsLocked(mergedTargets, shortcutType);
+            persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(shortcutType),
                     UserHandle.USER_SYSTEM, mergedTargets, str -> str);
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             onUserStateChangedLocked(userState);
         }
     }
 
-    /**
-     * Merges the old and restored value of
-     * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
-     *
-     * <p>Also clears out {@link R.string#config_defaultAccessibilityService} from
-     * the merged set if it was not present before restoring.
-     */
-    private void restoreAccessibilityShortcutTargetService(
-            String oldValue, String restoredValue) {
-        final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedStringToSet(oldValue, str -> str,
-                targetsFromSetting, /*doMerge=*/false);
-        final String defaultService =
-                mContext.getString(R.string.config_defaultAccessibilityService);
-        final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
-                ? null : ComponentName.unflattenFromString(defaultService);
-        boolean shouldClearDefaultService = defaultServiceComponent != null
-                && !stringSetContainsComponentName(targetsFromSetting, defaultServiceComponent);
-        readColonDelimitedStringToSet(restoredValue, str -> str,
-                targetsFromSetting, /*doMerge=*/true);
-        if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()) {
-            if (shouldClearDefaultService && stringSetContainsComponentName(
-                    targetsFromSetting, defaultServiceComponent)) {
-                Slog.i(LOG_TAG, "Removing default service " + defaultService
-                        + " from restore of "
-                        + Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
-                targetsFromSetting.removeIf(str ->
-                        defaultServiceComponent.equals(ComponentName.unflattenFromString(str)));
-            }
-            if (targetsFromSetting.isEmpty()) {
-                return;
-            }
-        }
-        synchronized (mLock) {
-            final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
-            final Set<String> shortcutTargets =
-                    userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
-            shortcutTargets.clear();
-            shortcutTargets.addAll(targetsFromSetting);
-            persistColonDelimitedSetToSettingLocked(
-                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                    UserHandle.USER_SYSTEM, targetsFromSetting, str -> str);
-            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-            onUserStateChangedLocked(userState);
-        }
+    private String getRawShortcutSetting(int userId, @UserShortcutType int shortcutType) {
+        return Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                ShortcutUtils.convertToKey(shortcutType), userId);
     }
 
+
     /**
      * Returns {@code true} if the set contains the provided non-null {@link ComponentName}.
      *
@@ -2263,7 +2232,7 @@
     private void showAccessibilityTargetsSelection(int displayId,
             @UserShortcutType int shortcutType) {
         final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
-        final String chooserClassName = (shortcutType == UserShortcutType.HARDWARE)
+        final String chooserClassName = (shortcutType == HARDWARE)
                 ? AccessibilityShortcutChooserActivity.class.getName()
                 : AccessibilityButtonChooserActivity.class.getName();
         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
@@ -3236,9 +3205,9 @@
         somethingChanged |= readAudioDescriptionEnabledSettingLocked(userState);
         somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
-        somethingChanged |= readAccessibilityShortcutKeySettingLocked(userState);
-        somethingChanged |= readAccessibilityQsTargetsLocked(userState);
-        somethingChanged |= readAccessibilityButtonTargetsLocked(userState);
+        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, HARDWARE);
+        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS);
+        somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE);
         somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState);
         somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState);
         somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
@@ -3386,60 +3355,34 @@
         userState.setSendMotionEventsEnabled(sendMotionEvents);
     }
 
-    private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
-        final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
+    /**
+     * Throws an exception for {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} types.
+     */
+    private boolean readAccessibilityShortcutTargetsLocked(AccessibilityUserState userState,
+            @UserShortcutType int shortcutType) {
+        assertNoTapShortcut(shortcutType);
+        final String settingValue = getRawShortcutSetting(userState.mUserId, shortcutType);
         final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
-        // Fall back to device's default a11y service, only when setting is never updated.
-        if (settingValue == null) {
+        // If dealing with an empty hardware shortcut, fall back to the default value.
+        if (shortcutType == HARDWARE && settingValue == null) {
             final String defaultService = mContext.getString(
                     R.string.config_defaultAccessibilityService);
             if (!TextUtils.isEmpty(defaultService)) {
-                targetsFromSetting.add(defaultService);
+                // Convert to component name to reformat the target if it has a relative path.
+                ComponentName name = ComponentName.unflattenFromString(defaultService);
+                if (name != null) {
+                    targetsFromSetting.add(name.flattenToString());
+                }
             }
+        } else {
+            readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false);
         }
 
-        final Set<String> currentTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
-        if (targetsFromSetting.equals(currentTargets)) {
-            return false;
+        if (userState.updateShortcutTargetsLocked(targetsFromSetting, shortcutType)) {
+            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+            return true;
         }
-        currentTargets.clear();
-        currentTargets.addAll(targetsFromSetting);
-        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-        return true;
-    }
-
-    private boolean readAccessibilityQsTargetsLocked(AccessibilityUserState userState) {
-        final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_QS_TARGETS,
-                userState.mUserId, str -> str, targetsFromSetting);
-
-        final Set<String> currentTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
-        if (targetsFromSetting.equals(currentTargets)) {
-            return false;
-        }
-        userState.updateA11yQsTargetLocked(targetsFromSetting);
-        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-        return true;
-    }
-
-    private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
-        final Set<String> targetsFromSetting = new ArraySet<>();
-        readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                userState.mUserId, str -> str, targetsFromSetting);
-
-        final Set<String> currentTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
-        if (targetsFromSetting.equals(currentTargets)) {
-            return false;
-        }
-        currentTargets.clear();
-        currentTargets.addAll(targetsFromSetting);
-        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-        return true;
+        return false;
     }
 
     private boolean readAccessibilityButtonTargetComponentLocked(AccessibilityUserState userState) {
@@ -3487,14 +3430,10 @@
      */
     private void updateAccessibilityShortcutKeyTargetsLocked(AccessibilityUserState userState) {
         final Set<String> currentTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
-        final int lastSize = currentTargets.size();
-        if (lastSize == 0) {
-            return;
-        }
+                userState.getShortcutTargetsLocked(HARDWARE);
         currentTargets.removeIf(
                 name -> !userState.isShortcutTargetInstalledLocked(name));
-        if (lastSize == currentTargets.size()) {
+        if (!userState.updateShortcutTargetsLocked(currentTargets, HARDWARE)) {
             return;
         }
 
@@ -3680,13 +3619,9 @@
 
         final Set<String> currentTargets =
                 userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
-        final int lastSize = currentTargets.size();
-        if (lastSize == 0) {
-            return;
-        }
         currentTargets.removeIf(
                 name -> !userState.isShortcutTargetInstalledLocked(name));
-        if (lastSize == currentTargets.size()) {
+        if (!userState.updateShortcutTargetsLocked(currentTargets, SOFTWARE)) {
             return;
         }
 
@@ -3719,8 +3654,7 @@
             return;
         }
         final Set<String> buttonTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.SOFTWARE);
-        int lastSize = buttonTargets.size();
+                userState.getShortcutTargetsLocked(SOFTWARE);
         buttonTargets.removeIf(name -> {
             if (packageName != null && name != null && !name.contains(packageName)) {
                 return false;
@@ -3752,13 +3686,11 @@
             }
             return false;
         });
-        boolean changed = (lastSize != buttonTargets.size());
-        lastSize = buttonTargets.size();
 
         final Set<String> shortcutKeyTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.HARDWARE);
+                userState.getShortcutTargetsLocked(HARDWARE);
         final Set<String> qsShortcutTargets =
-                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+                userState.getShortcutTargetsLocked(QUICK_SETTINGS);
         userState.mEnabledServices.forEach(componentName -> {
             if (packageName != null && componentName != null
                     && !packageName.equals(componentName.getPackageName())) {
@@ -3790,8 +3722,7 @@
                     + " should be assign to the button or shortcut.");
             buttonTargets.add(serviceName);
         });
-        changed |= (lastSize != buttonTargets.size());
-        if (!changed) {
+        if (!userState.updateShortcutTargetsLocked(buttonTargets, SOFTWARE)) {
             return;
         }
 
@@ -3815,10 +3746,10 @@
         }
 
         final Set<String> targets =
-                userState.getShortcutTargetsLocked(UserShortcutType.QUICK_SETTINGS);
+                userState.getShortcutTargetsLocked(QUICK_SETTINGS);
 
         // Removes the targets that are no longer installed on the device.
-        boolean somethingChanged = targets.removeIf(
+        targets.removeIf(
                 name -> !userState.isShortcutTargetInstalledLocked(name));
         // Add the target if the a11y service is enabled and the tile exist in QS panel
         Set<ComponentName> enabledServices = userState.getEnabledServicesLocked();
@@ -3829,14 +3760,13 @@
             ComponentName tileService =
                     a11yFeatureToTileService.getOrDefault(enabledService, null);
             if (tileService != null && currentA11yTilesInQsPanel.contains(tileService)) {
-                somethingChanged |= targets.add(enabledService.flattenToString());
+                targets.add(enabledService.flattenToString());
             }
         }
 
-        if (!somethingChanged) {
+        if (!userState.updateShortcutTargetsLocked(targets, QUICK_SETTINGS)) {
             return;
         }
-        userState.updateA11yQsTargetLocked(targets);
 
         // Update setting key with new value.
         persistColonDelimitedSetToSettingLocked(
@@ -3862,14 +3792,14 @@
 
         final List<Pair<Integer, String>> shortcutTypeAndShortcutSetting = new ArrayList<>(3);
         shortcutTypeAndShortcutSetting.add(
-                new Pair<>(UserShortcutType.HARDWARE,
+                new Pair<>(HARDWARE,
                         Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
         shortcutTypeAndShortcutSetting.add(
                 new Pair<>(UserShortcutType.SOFTWARE,
                         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS));
         if (android.view.accessibility.Flags.a11yQsShortcut()) {
             shortcutTypeAndShortcutSetting.add(
-                    new Pair<>(UserShortcutType.QUICK_SETTINGS,
+                    new Pair<>(QUICK_SETTINGS,
                             Settings.Secure.ACCESSIBILITY_QS_TARGETS));
         }
 
@@ -3883,7 +3813,7 @@
                         shortcutSettingName,
                         userState.mUserId, currentTargets, str -> str);
 
-                if (shortcutType != UserShortcutType.QUICK_SETTINGS) {
+                if (shortcutType != QUICK_SETTINGS) {
                     continue;
                 }
 
@@ -3968,7 +3898,7 @@
 
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::performAccessibilityShortcutInternal, this,
-                Display.DEFAULT_DISPLAY, UserShortcutType.HARDWARE, targetName));
+                Display.DEFAULT_DISPLAY, HARDWARE, targetName));
     }
 
     /**
@@ -4115,7 +4045,7 @@
             final boolean requestA11yButton = (installedServiceInfo.flags
                     & FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
             // Turns on / off the accessibility service
-            if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == UserShortcutType.HARDWARE)
+            if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == HARDWARE)
                     || (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
                 if (serviceConnection == null) {
                     logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -4129,7 +4059,7 @@
                 }
                 return true;
             }
-            if (shortcutType == UserShortcutType.HARDWARE && targetSdk > Build.VERSION_CODES.Q
+            if (shortcutType == HARDWARE && targetSdk > Build.VERSION_CODES.Q
                     && requestA11yButton) {
                 if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
                     enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
@@ -4222,7 +4152,7 @@
             validNewTargets = newTargets;
 
             // filter out targets that doesn't have qs shortcut
-            if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+            if (shortcutType == QUICK_SETTINGS) {
                 validNewTargets = newTargets.stream().filter(target -> {
                     ComponentName targetComponent = ComponentName.unflattenFromString(target);
                     return featureToTileMap.containsKey(targetComponent);
@@ -4240,10 +4170,10 @@
                     /* defaultEmptyString= */ ""
             );
 
-            if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+            if (shortcutType == QUICK_SETTINGS) {
                 int numOfFeatureChanged = Math.abs(currentTargets.size() - validNewTargets.size());
                 logMetricForQsShortcutConfiguration(enable, numOfFeatureChanged);
-                userState.updateA11yQsTargetLocked(validNewTargets);
+                userState.updateShortcutTargetsLocked(validNewTargets, QUICK_SETTINGS);
                 scheduleNotifyClientsOfServicesStateChangeLocked(userState);
                 onUserStateChangedLocked(userState);
             }
@@ -4257,7 +4187,7 @@
         }
 
         // Add or Remove tile in QS Panel
-        if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
+        if (shortcutType == QUICK_SETTINGS) {
             mMainHandler.sendMessage(obtainMessage(
                     AccessibilityManagerService::updateA11yTileServicesInQuickSettingsPanel,
                     this, validNewTargets, currentTargets, userId));
@@ -4266,7 +4196,7 @@
         if (!enable) {
             return;
         }
-        if (shortcutType == UserShortcutType.HARDWARE) {
+        if (shortcutType == HARDWARE) {
             skipVolumeShortcutDialogTimeoutRestriction(userId);
             if (com.android.server.accessibility.Flags.enableHardwareShortcutDisablesWarning()) {
                 persistIntToSetting(
@@ -4461,6 +4391,7 @@
                     shortcutTargets.add(serviceName);
                 }
             }
+            userState.updateShortcutTargetsLocked(Set.copyOf(shortcutTargets), shortcutType);
             return shortcutTargets;
         }
     }
@@ -5672,7 +5603,7 @@
                         || mShowImeWithHardKeyboardUri.equals(uri)) {
                     userState.reconcileSoftKeyboardModeWithSettingsLocked();
                 } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
-                    if (readAccessibilityShortcutKeySettingLocked(userState)) {
+                    if (readAccessibilityShortcutTargetsLocked(userState, HARDWARE)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
@@ -5680,7 +5611,7 @@
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAccessibilityButtonTargetsUri.equals(uri)) {
-                    if (readAccessibilityButtonTargetsLocked(userState)) {
+                    if (readAccessibilityShortcutTargetsLocked(userState, SOFTWARE)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mUserNonInteractiveUiTimeoutUri.equals(uri)
@@ -6505,4 +6436,10 @@
         String metricId = enable ? METRIC_ID_QS_SHORTCUT_ADD : METRIC_ID_QS_SHORTCUT_REMOVE;
         Counter.logIncrementWithUid(metricId, Binder.getCallingUid(), numOfFeatures);
     }
+
+    private void assertNoTapShortcut(@UserShortcutType int shortcutType) {
+        if ((shortcutType & (TRIPLETAP | TWOFINGER_DOUBLETAP)) != 0) {
+            throw new IllegalArgumentException("Tap shortcuts are not supported.");
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index a37a184..de1c86a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -777,12 +777,15 @@
      * @return The array set of the strings
      */
     public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) {
+        return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType));
+    }
+    private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
         if (shortcutType == UserShortcutType.HARDWARE) {
             return mAccessibilityShortcutKeyTargets;
         } else if (shortcutType == UserShortcutType.SOFTWARE) {
             return mAccessibilityButtonTargets;
         } else if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
-            return getA11yQsTargets();
+            return mAccessibilityQsTargets;
         } else if ((shortcutType == UserShortcutType.TRIPLETAP
                 && isMagnificationSingleFingerTripleTapEnabledLocked()) || (
                 shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP
@@ -795,6 +798,32 @@
     }
 
     /**
+     * Updates the corresponding shortcut targets with the provided set.
+     * Tap shortcuts don't operate using sets of targets,
+     * so trying to update {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP}
+     * will instead throw an {@code IllegalArgumentException}
+     * @param newTargets set of targets to replace the existing set.
+     * @param shortcutType type to be replaced.
+     * @return {@code true} if the set was changed, or {@code false} if the elements are the same.
+     * @throws IllegalArgumentException if {@code TRIPLETAP} or {@code TWOFINGER_DOUBLETAP} is used.
+     */
+    boolean updateShortcutTargetsLocked(
+            Set<String> newTargets, @UserShortcutType int shortcutType) {
+        final int mask = UserShortcutType.TRIPLETAP | UserShortcutType.TWOFINGER_DOUBLETAP;
+        if ((shortcutType & mask) != 0) {
+            throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
+        }
+
+        final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType);
+        if (newTargets.equals(currentTargets)) {
+            return false;
+        }
+        currentTargets.clear();
+        currentTargets.addAll(newTargets);
+        return true;
+    }
+
+    /**
      * Whether or not the given shortcut target is installed in device.
      *
      * @param name The shortcut target name
@@ -844,8 +873,9 @@
             );
         }
 
-        Set<String> targets = getShortcutTargetsLocked(shortcutType);
-        boolean result = targets.removeIf(name -> {
+        // getting internal set lets us directly modify targets, as it's not a copy.
+        Set<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+        return targets.removeIf(name -> {
             ComponentName componentName;
             if (name == null
                     || (componentName = ComponentName.unflattenFromString(name)) == null) {
@@ -853,11 +883,6 @@
             }
             return componentName.equals(target);
         });
-        if (shortcutType == UserShortcutType.QUICK_SETTINGS) {
-            updateA11yQsTargetLocked(targets);
-        }
-
-        return result;
     }
 
     /**
@@ -1114,11 +1139,6 @@
         );
     }
 
-    public void updateA11yQsTargetLocked(Set<String> targets) {
-        mAccessibilityQsTargets.clear();
-        mAccessibilityQsTargets.addAll(targets);
-    }
-
     /**
      * Returns a copy of the targets which has qs shortcut turned on
      */
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 363a4a7..91a4d6f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -1126,7 +1126,7 @@
         }
     }
 
-    private void sendHostEndpointConnectedEvent() {
+    void sendHostEndpointConnectedEvent() {
         HostEndpointInfo info = new HostEndpointInfo();
         info.hostEndpointId = (char) mHostEndPointId;
         info.packageName = mPackage;
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index b3fb147..7a722bc 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -74,6 +74,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -158,10 +159,8 @@
 
     // A queue of reliable message records for duplicate detection
     private final PriorityQueue<ReliableMessageRecord> mReliableMessageRecordQueue =
-            new PriorityQueue<ReliableMessageRecord>(
-                    (ReliableMessageRecord left, ReliableMessageRecord right) -> {
-                        return Long.compare(left.getTimestamp(), right.getTimestamp());
-                    });
+            new PriorityQueue<>(
+                    Comparator.comparingLong(ReliableMessageRecord::getTimestamp));
 
     // The test mode manager that manages behaviors during test mode.
     private final TestModeManager mTestModeManager = new TestModeManager();
@@ -179,10 +178,10 @@
     private boolean mIsBtMainEnabled = false;
 
     // True if test mode is enabled for the Context Hub
-    private AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
+    private final AtomicBoolean mIsTestModeEnabled = new AtomicBoolean(false);
 
     // A hashmap used to record if a contexthub is waiting for daily query
-    private Set<Integer> mMetricQueryPendingContextHubIds =
+    private final Set<Integer> mMetricQueryPendingContextHubIds =
             Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
 
     // Lock object for sendWifiSettingUpdate()
@@ -242,10 +241,14 @@
 
         @Override
         public void handleServiceRestart() {
-            Log.i(TAG, "Starting Context Hub Service restart");
+            Log.i(TAG, "Recovering from Context Hub HAL restart...");
             initExistingCallbacks();
             resetSettings();
-            Log.i(TAG, "Finished Context Hub Service restart");
+            if (Flags.reconnectHostEndpointsAfterHalRestart()) {
+                mClientManager.forEachClientOfHub(mContextHubId,
+                        ContextHubClientBroker::sendHostEndpointConnectedEvent);
+            }
+            Log.i(TAG, "Finished recovering from Context Hub HAL restart");
         }
 
         @Override
@@ -317,11 +320,11 @@
          */
         private static final int MAX_PROBABILITY_PERCENT = 100;
 
-        private Random mRandom = new Random();
+        private final Random mRandom = new Random();
 
         /**
-         * @see ContextHubServiceCallback.handleNanoappMessage
          * @return whether the message was handled
+         * @see ContextHubServiceCallback#handleNanoappMessage
          */
         public boolean handleNanoappMessage(int contextHubId,
                 short hostEndpointId, NanoAppMessage message,
@@ -331,7 +334,8 @@
             }
 
             if (Flags.reliableMessageDuplicateDetectionService()
-                && didEventHappen(MESSAGE_DUPLICATION_PROBABILITY_PERCENT)) {
+                    && mRandom.nextInt(MAX_PROBABILITY_PERCENT)
+                    < MESSAGE_DUPLICATION_PROBABILITY_PERCENT) {
                 Log.i(TAG, "[TEST MODE] Duplicating message ("
                         + NUM_MESSAGES_TO_DUPLICATE
                         + " sends) with message sequence number: "
@@ -344,16 +348,6 @@
             }
             return false;
         }
-
-        /**
-         * Returns true if the event with percentPercent did happen.
-         *
-         * @param probabilityPercent the percent probability of the event.
-         * @return true if the event happened, false otherwise.
-         */
-        private boolean didEventHappen(int probabilityPercent) {
-            return mRandom.nextInt(MAX_PROBABILITY_PERCENT) < probabilityPercent;
-        }
     }
 
     public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) {
@@ -476,7 +470,7 @@
             hubInfo = mContextHubWrapper.getHubs();
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException while getting Context Hub info", e);
-            hubInfo = new Pair(Collections.emptyList(), Collections.emptyList());
+            hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList());
         }
 
         long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
@@ -536,6 +530,7 @@
         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
             try {
                 mContextHubWrapper.registerExistingCallback(contextHubId);
+                Log.i(TAG, "Re-registered callback to context hub " + contextHubId);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException while registering existing service callback for hub "
                         + "(ID = " + contextHubId + ")", e);
@@ -647,7 +642,7 @@
         mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers(
                 SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> {
                     // If we are in HSUM mode, any user can change the microphone setting
-                    if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
+                    if (UserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) {
                         Log.d(TAG, "User: " + userId + " mic privacy: " + enabled);
                         sendMicrophoneDisableSettingUpdate(enabled);
                     }
@@ -720,33 +715,30 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public int[] getContextHubHandles() throws RemoteException {
+    public int[] getContextHubHandles() {
         super.getContextHubHandles_enforcePermission();
-
         return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
+    public ContextHubInfo getContextHubInfo(int contextHubHandle) {
         super.getContextHubInfo_enforcePermission();
-
         if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
             return null;
         }
-
         return mContextHubIdToInfoMap.get(contextHubHandle);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Returns a List of ContextHubInfo object describing the available hubs.
      *
      * @return the List of ContextHubInfo objects
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public List<ContextHubInfo> getContextHubs() throws RemoteException {
+    public List<ContextHubInfo> getContextHubs() {
         super.getContextHubs_enforcePermission();
 
         return mContextHubInfoList;
@@ -814,7 +806,7 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
+    public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) {
         super.loadNanoApp_enforcePermission();
 
         if (mContextHubWrapper == null) {
@@ -843,7 +835,7 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
+    public int unloadNanoApp(int nanoAppHandle) {
         super.unloadNanoApp_enforcePermission();
 
         if (mContextHubWrapper == null) {
@@ -870,7 +862,7 @@
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
+    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
 
         super.getNanoAppInstanceInfo_enforcePermission();
 
@@ -880,7 +872,7 @@
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public int[] findNanoAppOnHub(
-            int contextHubHandle, NanoAppFilter filter) throws RemoteException {
+            int contextHubHandle, NanoAppFilter filter) {
 
         super.findNanoAppOnHub_enforcePermission();
 
@@ -895,20 +887,19 @@
 
         int[] retArray = new int[foundInstances.size()];
         for (int i = 0; i < foundInstances.size(); i++) {
-            retArray[i] = foundInstances.get(i).intValue();
+            retArray[i] = foundInstances.get(i);
         }
         return retArray;
     }
 
     /**
      * Performs a query at the specified hub.
-     * <p>
-     * This method should only be invoked internally by the service, either to update the service
+     *
+     * <p>This method should only be invoked internally by the service, either to update the service
      * cache or as a result of an explicit query requested by a client through the sendMessage API.
      *
      * @param contextHubId the ID of the hub to do the query
      * @return true if the query succeeded
-     * @throws IllegalStateException if the transaction queue is full
      */
     private boolean queryNanoAppsInternal(int contextHubId) {
         if (mContextHubWrapper == null) {
@@ -1003,7 +994,7 @@
             return;
         }
 
-        byte errorCode = ErrorCode.OK;
+        byte errorCode;
         synchronized (mReliableMessageRecordQueue) {
             Optional<ReliableMessageRecord> record =
                     findReliableMessageRecord(contextHubId,
@@ -1219,7 +1210,6 @@
         return mContextHubIdToInfoMap.containsKey(contextHubId);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Creates and registers a client at the service for the specified Context Hub.
      *
@@ -1232,10 +1222,11 @@
      * @throws IllegalStateException    if max number of clients have already registered
      * @throws NullPointerException     if clientCallback is null
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public IContextHubClient createClient(
             int contextHubId, IContextHubClientCallback clientCallback,
-            @Nullable String attributionTag, String packageName) throws RemoteException {
+            @Nullable String attributionTag, String packageName) {
         super.createClient_enforcePermission();
 
         if (!isValidContextHubId(contextHubId)) {
@@ -1250,7 +1241,6 @@
                 contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Creates and registers a PendingIntent client at the service for the specified Context Hub.
      *
@@ -1262,10 +1252,11 @@
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public IContextHubClient createPendingIntentClient(
             int contextHubId, PendingIntent pendingIntent, long nanoAppId,
-            @Nullable String attributionTag) throws RemoteException {
+            @Nullable String attributionTag) {
         super.createPendingIntentClient_enforcePermission();
 
         if (!isValidContextHubId(contextHubId)) {
@@ -1277,15 +1268,14 @@
                 contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Loads a nanoapp binary at the specified Context hub.
      *
      * @param contextHubId        the ID of the hub to load the binary
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppBinary       the binary to load
-     * @throws IllegalStateException if the transaction queue is full
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public void loadNanoAppOnHub(
             int contextHubId, IContextHubTransactionCallback transactionCallback,
@@ -1308,15 +1298,14 @@
         mTransactionManager.addTransaction(transaction);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Unloads a nanoapp from the specified Context Hub.
      *
      * @param contextHubId        the ID of the hub to unload the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to unload
-     * @throws IllegalStateException if the transaction queue is full
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public void unloadNanoAppFromHub(
             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
@@ -1333,19 +1322,17 @@
         mTransactionManager.addTransaction(transaction);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Enables a nanoapp at the specified Context Hub.
      *
      * @param contextHubId        the ID of the hub to enable the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to enable
-     * @throws IllegalStateException if the transaction queue is full
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public void enableNanoApp(
-            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
-            throws RemoteException {
+            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
         super.enableNanoApp_enforcePermission();
 
         if (!checkHalProxyAndContextHubId(
@@ -1358,19 +1345,17 @@
         mTransactionManager.addTransaction(transaction);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Disables a nanoapp at the specified Context Hub.
      *
      * @param contextHubId        the ID of the hub to disable the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
      * @param nanoAppId           the ID of the nanoapp to disable
-     * @throws IllegalStateException if the transaction queue is full
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public void disableNanoApp(
-            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
-            throws RemoteException {
+            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) {
         super.disableNanoApp_enforcePermission();
 
         if (!checkHalProxyAndContextHubId(
@@ -1383,17 +1368,16 @@
         mTransactionManager.addTransaction(transaction);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Queries for a list of nanoapps from the specified Context hub.
      *
      * @param contextHubId        the ID of the hub to query
      * @param transactionCallback the client-facing transaction callback interface
-     * @throws IllegalStateException if the transaction queue is full
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
-            throws RemoteException {
+    public void queryNanoApps(int contextHubId,
+            IContextHubTransactionCallback transactionCallback) {
         super.queryNanoApps_enforcePermission();
 
         if (!checkHalProxyAndContextHubId(
@@ -1406,16 +1390,15 @@
         mTransactionManager.addTransaction(transaction);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
      *
      * @param hubInfo The Context Hub to query a list of nanoapps from.
      * @return The list of 64-bit IDs of the preloaded nanoapps.
-     * @throws NullPointerException if hubInfo is null
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
-    public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+    public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) {
         super.getPreloadedNanoAppIds_enforcePermission();
         Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
 
@@ -1426,7 +1409,6 @@
         return nanoappIds;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     /**
      * Puts the context hub in and out of test mode. Test mode is a clean state
      * where tests can be executed in the same environment. If enable is true,
@@ -1442,6 +1424,7 @@
      *               test mode.
      * @return       If true, the operation was successful; false otherwise.
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Override
     public boolean setTestMode(boolean enable) {
         super.setTestMode_enforcePermission();
@@ -1551,10 +1534,6 @@
         }
     }
 
-    private void checkPermissions() {
-        ContextHubServiceUtil.checkPermissions(mContext);
-    }
-
     private int onMessageReceiptOldApi(
             int msgType, int contextHubHandle, int appInstance, byte[] data) {
         if (data == null) {
@@ -1586,7 +1565,6 @@
                     callback.onMessageReceipt(contextHubHandle, appInstance, msg);
                 } catch (RemoteException e) {
                     Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
-                    continue;
                 }
             }
             mCallbacksList.finishBroadcast();
@@ -1729,8 +1707,8 @@
      * Hub.
      */
     private void sendMicrophoneDisableSettingUpdateForCurrentUser() {
-        boolean isEnabled = mSensorPrivacyManagerInternal == null ? false :
-                mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
+        boolean isEnabled = mSensorPrivacyManagerInternal != null
+                && mSensorPrivacyManagerInternal.isSensorPrivacyEnabled(
                 getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE);
         sendMicrophoneDisableSettingUpdate(isEnabled);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f297708..a4f534e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8565,6 +8565,13 @@
          */
         private boolean enqueueNotification() {
             synchronized (mNotificationLock) {
+                if (android.app.Flags.secureAllowlistToken()) {
+                    // allowlistToken is populated by unparceling, so it will be absent if the
+                    // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+                    // summaries) instead of via notify(). Fix that.
+                    r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
+                }
+
                 final long snoozeAt =
                         mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
                                 r.getUser().getIdentifier(),
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0a384e5..21155bb 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -11154,8 +11154,7 @@
             boolean cancel) {
         // This override is just for getting metrics. allFinished needs to be checked before
         // finish because finish resets all the states.
-        final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
-        if (syncGroup != null && group != getSyncGroup()) return;
+        if (isDifferentSyncGroup(group)) return;
         mLastAllReadyAtSync = allSyncFinished();
         super.finishSync(outMergedTransaction, group, cancel);
     }
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index e8faff6..a8cc2ae 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -348,6 +348,11 @@
                 wc.setSyncGroup(this);
             }
             wc.prepareSync();
+            if (wc.mSyncState == WindowContainer.SYNC_STATE_NONE && wc.mSyncGroup != null) {
+                Slog.w(TAG, "addToSync: unset SyncGroup " + wc.mSyncGroup.mSyncId
+                        + " for non-sync " + wc);
+                wc.mSyncGroup = null;
+            }
             if (mReady) {
                 mWm.mWindowPlacerLocked.requestTraversal();
             }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c72087b..9b8c038 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -47,8 +47,6 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.SurfaceControl.METADATA_TASK_ID;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -3586,15 +3584,29 @@
                 ? null : new PictureInPictureParams(top.pictureInPictureArgs);
     }
 
-    Rect getDisplayCutoutInsets() {
-        if (mDisplayContent == null || getDisplayInfo().displayCutout == null) return null;
+    /** @return The display cutout insets where the main window is not allowed to extend to. */
+    @NonNull Rect getDisplayCutoutInsets() {
+        final Rect displayCutoutInsets = new Rect();
+        if (mDisplayContent == null || getDisplayInfo().displayCutout == null) {
+            return displayCutoutInsets;
+        }
         final WindowState w = getTopVisibleAppMainWindow();
-        final int displayCutoutMode = w == null
-                ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-                : w.getAttrs().layoutInDisplayCutoutMode;
-        return (displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
-                || displayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
-                ? null : getDisplayInfo().displayCutout.getSafeInsets();
+        final Rect displayFrame;
+        if (w != null && w.mHaveFrame) {
+            displayFrame = w.getDisplayFrame();
+        } else {
+            displayFrame = mDisplayContent.getBounds();
+            displayFrame.inset(getDisplayInfo().displayCutout.getSafeInsets());
+        }
+        final Rect taskBounds = getBounds();
+        if (displayCutoutInsets.setIntersect(taskBounds, displayFrame)) {
+            displayCutoutInsets.set(
+                    displayCutoutInsets.left - taskBounds.left,
+                    displayCutoutInsets.top - taskBounds.top,
+                    taskBounds.right - displayCutoutInsets.right,
+                    taskBounds.bottom - displayCutoutInsets.bottom);
+        }
+        return displayCutoutInsets;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6dbd259..1f31af6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3986,6 +3986,19 @@
     }
 
     /**
+     * Returns {@code true} if this window container belongs to a different sync group than the
+     * given group.
+     */
+    boolean isDifferentSyncGroup(@Nullable BLASTSyncEngine.SyncGroup group) {
+        if (group == null) return false;
+        final BLASTSyncEngine.SyncGroup thisGroup = getSyncGroup();
+        if (thisGroup == null || group == thisGroup) return false;
+        Slog.d(TAG, this + " uses a different SyncGroup, current=" + thisGroup.mSyncId
+                + " given=" + group.mSyncId);
+        return true;
+    }
+
+    /**
      * Recursively finishes/cleans-up sync state of this subtree and collects all the sync
      * transactions into `outMergedTransaction`.
      * @param outMergedTransaction A transaction to merge all the recorded sync operations into.
@@ -3994,10 +4007,14 @@
      */
     void finishSync(Transaction outMergedTransaction, @Nullable BLASTSyncEngine.SyncGroup group,
             boolean cancel) {
-        if (mSyncState == SYNC_STATE_NONE) return;
-        final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
-        // If it's null, then we need to clean-up anyways.
-        if (syncGroup != null && group != syncGroup) return;
+        if (mSyncState == SYNC_STATE_NONE) {
+            if (mSyncGroup != null) {
+                Slog.e(TAG, "finishSync: stale group " + mSyncGroup.mSyncId + " of " + this);
+                mSyncGroup = null;
+            }
+            return;
+        }
+        if (isDifferentSyncGroup(group)) return;
         ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
         outMergedTransaction.merge(mSyncTransaction);
         for (int i = mChildren.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dcd4bd6..9d4a3b8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -96,6 +96,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+import static android.util.SequenceUtils.getNextSeq;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
@@ -3652,6 +3653,7 @@
             }
         }
         outFrames.compatScale = getCompatScaleForClient();
+        outFrames.seq = getNextSeq(mLastReportedFrames.seq);
         if (mLastReportedFrames != outFrames) {
             mLastReportedFrames.setTo(outFrames);
         }
@@ -3682,7 +3684,9 @@
     }
 
     void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
+        final int lastSeq = mLastReportedInsetsState.getSeq();
         outInsetsState.set(getCompatInsetsState(), copySources);
+        outInsetsState.setSeq(getNextSeq(lastSeq));
         if (outInsetsState != mLastReportedInsetsState) {
             // No need to copy for the recorded.
             mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
@@ -3691,9 +3695,11 @@
 
     void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
             boolean copyControls) {
+        final int lastSeq = mLastReportedInsetsState.getSeq();
         final InsetsSourceControl[] controls =
                 getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
         outArray.set(controls, copyControls);
+        outArray.setSeq(getNextSeq(lastSeq));
         if (outArray != mLastReportedActiveControls) {
             // No need to copy for the recorded.
             mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
@@ -5791,8 +5797,7 @@
     @Override
     void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
             boolean cancel) {
-        final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
-        if (syncGroup != null && group != syncGroup) return;
+        if (isDifferentSyncGroup(group)) return;
         mPrepareSyncSeqId = 0;
         if (cancel) {
             // This is leaving sync so any buffers left in the sync have a chance of
diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp
index 148054b..d44bb5a 100644
--- a/services/fakes/Android.bp
+++ b/services/fakes/Android.bp
@@ -16,5 +16,5 @@
         "java/**/*.java",
     ],
     path: "java",
-    visibility: ["//frameworks/base"],
+    visibility: ["//frameworks/base/ravenwood:__subpackages__"],
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 4e8c755..9884085 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -25,6 +25,8 @@
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
 import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER;
 
@@ -1082,7 +1084,7 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.HARDWARE,
+                HARDWARE,
                 List.of(target),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1346,14 +1348,14 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.HARDWARE,
+                HARDWARE,
                 List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
-                        mTestableContext, ShortcutConstants.UserShortcutType.HARDWARE,
+                        mTestableContext, HARDWARE,
                         TARGET_STANDARD_A11Y_SERVICE.flattenToString())
         ).isTrue();
     }
@@ -1367,7 +1369,7 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                UserShortcutType.HARDWARE,
+                HARDWARE,
                 List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1375,7 +1377,7 @@
         assertThat(
                         ShortcutUtils.isComponentIdExistingInSettings(
                                 mTestableContext,
-                                ShortcutConstants.UserShortcutType.HARDWARE,
+                                HARDWARE,
                                 TARGET_STANDARD_A11Y_SERVICE.flattenToString()))
                 .isFalse();
     }
@@ -1390,14 +1392,14 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.QUICK_SETTINGS,
+                QUICK_SETTINGS,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
-                        mTestableContext, UserShortcutType.QUICK_SETTINGS,
+                        mTestableContext, QUICK_SETTINGS,
                         TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
         ).isTrue();
         verify(mStatusBarManagerInternal)
@@ -1417,14 +1419,14 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                UserShortcutType.QUICK_SETTINGS,
+                QUICK_SETTINGS,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
-                        mTestableContext, UserShortcutType.QUICK_SETTINGS,
+                        mTestableContext, QUICK_SETTINGS,
                         TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
         ).isFalse();
         verify(mStatusBarManagerInternal)
@@ -1614,44 +1616,49 @@
 
     @Test
     @EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
-    public void restoreAccessibilityQsTargets_a11yQsTargetsRestored() {
+    public void restoreShortcutTargets_qs_a11yQsTargetsRestored() {
         String daltonizerTile =
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
         String colorInversionTile =
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
-        userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+        userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
 
         broadcastSettingRestored(
-                Settings.Secure.ACCESSIBILITY_QS_TARGETS,
-                /*previousValue=*/null,
+                ShortcutUtils.convertToKey(QUICK_SETTINGS),
                 /*newValue=*/colorInversionTile);
 
-        assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM).getA11yQsTargets())
-                .containsExactlyElementsIn(Set.of(daltonizerTile, colorInversionTile));
+        Set<String> expected = Set.of(daltonizerTile, colorInversionTile);
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+                .containsExactlyElementsIn(expected);
+        assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+                .containsExactlyElementsIn(expected);
     }
 
     @Test
     @DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
-    public void restoreAccessibilityQsTargets_a11yQsTargetsNotRestored() {
+    public void restoreShortcutTargets_qs_a11yQsTargetsNotRestored() {
         String daltonizerTile =
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString();
         String colorInversionTile =
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString();
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
-        userState.updateA11yQsTargetLocked(Set.of(daltonizerTile));
+        userState.updateShortcutTargetsLocked(Set.of(daltonizerTile), QUICK_SETTINGS);
+        putShortcutSettingForUser(QUICK_SETTINGS, daltonizerTile, userState.mUserId);
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
 
         broadcastSettingRestored(
-                Settings.Secure.ACCESSIBILITY_QS_TARGETS,
-                /*previousValue=*/null,
+                ShortcutUtils.convertToKey(QUICK_SETTINGS),
                 /*newValue=*/colorInversionTile);
 
-        assertThat(userState.getA11yQsTargets())
-                .containsExactlyElementsIn(Set.of(daltonizerTile));
+        Set<String> expected = Set.of(daltonizerTile);
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(QUICK_SETTINGS)))
+                .containsExactlyElementsIn(expected);
+        assertThat(userState.getShortcutTargetsLocked(QUICK_SETTINGS))
+                .containsExactlyElementsIn(expected);
     }
 
     @Test
@@ -1717,27 +1724,26 @@
 
     @Test
     @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
-    public void restoreA11yShortcutTargetService_targetsMerged() {
+    public void restoreShortcutTargets_hardware_targetsMerged() {
+        mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         final String otherPrevious = TARGET_MAGNIFICATION;
-        final String combinedPrevious = String.join(":", servicePrevious, otherPrevious);
         final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
         setupShortcutTargetServices(userState);
+        mA11yms.enableShortcutsForTargets(
+                true, HARDWARE, List.of(servicePrevious, otherPrevious), userState.mUserId);
 
         broadcastSettingRestored(
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                /*previousValue=*/combinedPrevious,
+                ShortcutUtils.convertToKey(HARDWARE),
                 /*newValue=*/serviceRestored);
 
         final Set<String> expected = Set.of(servicePrevious, otherPrevious, serviceRestored);
-        assertThat(readStringsFromSetting(
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
                 .containsExactlyElementsIn(expected);
-        assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
-                .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+        assertThat(userState.getShortcutTargetsLocked(HARDWARE))
                 .containsExactlyElementsIn(expected);
     }
 
@@ -1745,7 +1751,7 @@
     @EnableFlags({
             android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
             Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
-    public void restoreA11yShortcutTargetService_alreadyHadDefaultService_doesNotClear() {
+    public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
         final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
         mTestableContext.getOrCreateTestableResources().addOverride(
                 R.string.config_defaultAccessibilityService, serviceDefault);
@@ -1754,17 +1760,18 @@
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
         setupShortcutTargetServices(userState);
 
+        // default is present in userState & setting, so it's not cleared
+        putShortcutSettingForUser(HARDWARE, serviceDefault, UserHandle.USER_SYSTEM);
+        userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+
         broadcastSettingRestored(
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                /*previousValue=*/serviceDefault,
                 /*newValue=*/serviceDefault);
 
         final Set<String> expected = Set.of(serviceDefault);
-        assertThat(readStringsFromSetting(
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
                 .containsExactlyElementsIn(expected);
-        assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
-                .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+        assertThat(userState.getShortcutTargetsLocked(HARDWARE))
                 .containsExactlyElementsIn(expected);
     }
 
@@ -1772,7 +1779,7 @@
     @EnableFlags({
             android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
             Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
-    public void restoreA11yShortcutTargetService_didNotHaveDefaultService_clearsDefaultService() {
+    public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
         final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
         final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         // Restored value from the broadcast contains both default and non-default service.
@@ -1784,18 +1791,45 @@
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
         setupShortcutTargetServices(userState);
 
-        broadcastSettingRestored(
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                /*previousValue=*/null,
+        broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
                 /*newValue=*/combinedRestored);
 
         // The default service is cleared from the final restored value.
         final Set<String> expected = Set.of(serviceRestored);
-        assertThat(readStringsFromSetting(
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
                 .containsExactlyElementsIn(expected);
-        assertThat(mA11yms.mUserStates.get(UserHandle.USER_SYSTEM)
-                .getShortcutTargetsLocked(UserShortcutType.HARDWARE))
+        assertThat(userState.getShortcutTargetsLocked(HARDWARE))
+                .containsExactlyElementsIn(expected);
+    }
+
+    @Test
+    @EnableFlags({
+            android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
+            Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+    public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
+        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+        final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
+        // Restored value from the broadcast contains both default and non-default service.
+        final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
+        mTestableContext.getOrCreateTestableResources().addOverride(
+                R.string.config_defaultAccessibilityService, serviceDefault);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+
+        // UserState has default, but setting is null (this emulates a typical scenario in SUW).
+        userState.updateShortcutTargetsLocked(Set.of(serviceDefault), HARDWARE);
+        putShortcutSettingForUser(HARDWARE, null, UserHandle.USER_SYSTEM);
+
+        broadcastSettingRestored(ShortcutUtils.convertToKey(HARDWARE),
+                /*newValue=*/combinedRestored);
+
+        // The default service is cleared from the final restored value.
+        final Set<String> expected = Set.of(serviceRestored);
+        assertThat(readStringsFromSetting(ShortcutUtils.convertToKey(HARDWARE)))
+                .containsExactlyElementsIn(expected);
+        assertThat(userState.getShortcutTargetsLocked(HARDWARE))
                 .containsExactlyElementsIn(expected);
     }
 
@@ -1806,11 +1840,10 @@
         return result;
     }
 
-    private void broadcastSettingRestored(String setting, String previousValue, String newValue) {
+    private void broadcastSettingRestored(String setting, String newValue) {
         Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 .putExtra(Intent.EXTRA_SETTING_NAME, setting)
-                .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
                 .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, newValue);
         sendBroadcastToAccessibilityManagerService(intent);
         mTestableLooper.processAllMessages();
@@ -1952,4 +1985,13 @@
     private static boolean isSameCurrentUser(AccessibilityManagerService service, Context context) {
         return service.getCurrentUserIdLocked() == context.getUserId();
     }
+
+    private void putShortcutSettingForUser(@UserShortcutType int shortcutType,
+            String shortcutValue, int userId) {
+        Settings.Secure.putStringForUser(
+                mTestableContext.getContentResolver(),
+                ShortcutUtils.convertToKey(shortcutType),
+                shortcutValue,
+                userId);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b269beb9..9fad14d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -28,6 +28,7 @@
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -429,20 +430,20 @@
     }
 
     @Test
-    public void updateA11yQsTargetLocked_valueUpdated() {
+    public void updateShortcutTargetsLocked_quickSettings_valueUpdated() {
         Set<String> newTargets = Set.of(
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(),
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString()
         );
 
-        mUserState.updateA11yQsTargetLocked(newTargets);
+        mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS);
 
         assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets);
     }
 
     @Test
     public void getA11yQsTargets_returnsCopiedData() {
-        updateA11yQsTargetLocked_valueUpdated();
+        updateShortcutTargetsLocked_quickSettings_valueUpdated();
 
         Set<String> targets = mUserState.getA11yQsTargets();
         targets.clear();
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 c1d7afb..c48d745 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -14335,6 +14335,29 @@
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
+    public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
+        Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentIntent(createPendingIntent("content"))
+                .build();
+        NotificationRecord record = new NotificationRecord(
+                mContext,
+                new StatusBarNotification(mPkg, mPkg, 1, "tag", mUid, 44, receivedWithoutParceling,
+                        mUser, "groupKey", 0),
+                mTestNotificationChannel);
+        assertThat(record.getNotification().getAllowlistToken()).isNull();
+
+        mWorkerHandler.post(
+                mService.new EnqueueNotificationRunnable(mUserId, record, false, false,
+                mPostNotificationTrackerFactory.newTracker(null)));
+        waitForIdle();
+
+        assertThat(mService.mNotificationList).hasSize(1);
+        assertThat(mService.mNotificationList.get(0).getNotification().getAllowlistToken())
+                .isEqualTo(NotificationManagerService.ALLOWLIST_TOKEN);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_rejectsOtherToken() throws RemoteException {
         Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 6c5f975..1c32980 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -33,6 +33,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -417,6 +418,22 @@
     }
 
     @Test
+    public void testSkipPrepareSync() {
+        final TestWindowContainer wc = new TestWindowContainer(mWm, true /* waiter */);
+        wc.mSkipPrepareSync = true;
+        final BLASTSyncEngine bse = createTestBLASTSyncEngine();
+        final BLASTSyncEngine.SyncGroup syncGroup = bse.prepareSyncSet(
+                mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
+        bse.startSyncSet(syncGroup);
+        bse.addToSyncSet(syncGroup.mSyncId, wc);
+        assertEquals(SYNC_STATE_NONE, wc.mSyncState);
+        // If the implementation of prepareSync doesn't set sync state, the sync group should also
+        // be empty.
+        assertNull(wc.mSyncGroup);
+        assertTrue(wc.isSyncFinished(syncGroup));
+    }
+
+    @Test
     public void testNonBlastMethod() {
         mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow");
 
@@ -694,6 +711,7 @@
         final boolean mWaiter;
         boolean mVisibleRequested = true;
         boolean mFillsParent = false;
+        boolean mSkipPrepareSync = false;
 
         TestWindowContainer(WindowManagerService wms, boolean waiter) {
             super(wms);
@@ -703,6 +721,9 @@
 
         @Override
         boolean prepareSync() {
+            if (mSkipPrepareSync) {
+                return false;
+            }
             if (!super.prepareSync()) {
                 return false;
             }
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index e29d321..5976657 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -51,10 +51,8 @@
 
 java_library {
     name: "android.test.mock.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-java"],
     srcs: [":android-test-mock-sources"],
-    visibility: [
-        "//frameworks/base",
-    ],
 }
 
 android_ravenwood_test {