Merge "Enable group child bg fix when redesign flag is on" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index fbc8eef..216bbab 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34264,6 +34264,7 @@
     method public boolean hasFileDescriptors();
     method public boolean hasFileDescriptors(int, int);
     method public byte[] marshall();
+    method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void marshall(@NonNull java.nio.ByteBuffer);
     method @NonNull public static android.os.Parcel obtain();
     method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
     method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
@@ -34333,6 +34334,7 @@
     method public void setDataSize(int);
     method public void setPropagateAllowBlocking();
     method public void unmarshall(@NonNull byte[], int, int);
+    method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void unmarshall(@NonNull java.nio.ByteBuffer);
     method public void writeArray(@Nullable Object[]);
     method public void writeBinderArray(@Nullable android.os.IBinder[]);
     method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 00fa1c1..bdecbae 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -302,13 +302,23 @@
 
     @Override
     public Intent getLaunchIntentForPackage(String packageName) {
+        return getLaunchIntentForPackage(packageName, false);
+    }
+
+    @Override
+    @Nullable
+    public Intent getLaunchIntentForPackage(@NonNull String packageName,
+            boolean includeDirectBootUnaware) {
+        ResolveInfoFlags queryFlags = ResolveInfoFlags.of(
+                includeDirectBootUnaware ? MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE : 0);
+
         // First see if the package has an INFO activity; the existence of
         // such an activity is implied to be the desired front-door for the
         // overall package (such as if it has multiple launcher entries).
         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_INFO);
         intentToResolve.setPackage(packageName);
-        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
+        List<ResolveInfo> ris = queryIntentActivities(intentToResolve, queryFlags);
 
         // Otherwise, try to find a main launcher activity.
         if (ris == null || ris.size() <= 0) {
@@ -316,7 +326,7 @@
             intentToResolve.removeCategory(Intent.CATEGORY_INFO);
             intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
             intentToResolve.setPackage(packageName);
-            ris = queryIntentActivities(intentToResolve, 0);
+            ris = queryIntentActivities(intentToResolve, queryFlags);
         }
         if (ris == null || ris.size() <= 0) {
             return null;
diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl
index feb21d6..92f7d52 100644
--- a/core/java/android/app/AutomaticZenRule.aidl
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -16,4 +16,6 @@
 
 package android.app;
 
-parcelable AutomaticZenRule;
\ No newline at end of file
+parcelable AutomaticZenRule;
+
+parcelable AutomaticZenRule.AzrWithId;
\ No newline at end of file
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index fa977c9..1ce38ac 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@
     public AutomaticZenRule(Parcel source) {
         enabled = source.readInt() == ENABLED;
         if (source.readInt() == ENABLED) {
-            name = getTrimmedString(source.readString());
+            name = getTrimmedString(source.readString8());
         }
         interruptionFilter = source.readInt();
         conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@
                 source.readParcelable(null, android.content.ComponentName.class));
         creationTime = source.readLong();
         mZenPolicy = source.readParcelable(null, ZenPolicy.class);
-        mPkg = source.readString();
+        mPkg = source.readString8();
         mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
         mAllowManualInvocation = source.readBoolean();
         mIconResId = source.readInt();
-        mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
+        mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
         mType = source.readInt();
     }
 
@@ -514,7 +514,7 @@
         dest.writeInt(enabled ? ENABLED : DISABLED);
         if (name != null) {
             dest.writeInt(1);
-            dest.writeString(name);
+            dest.writeString8(name);
         } else {
             dest.writeInt(0);
         }
@@ -524,11 +524,11 @@
         dest.writeParcelable(configurationActivity, 0);
         dest.writeLong(creationTime);
         dest.writeParcelable(mZenPolicy, 0);
-        dest.writeString(mPkg);
+        dest.writeString8(mPkg);
         dest.writeParcelable(mDeviceEffects, 0);
         dest.writeBoolean(mAllowManualInvocation);
         dest.writeInt(mIconResId);
-        dest.writeString(mTriggerDescription);
+        dest.writeString8(mTriggerDescription);
         dest.writeInt(mType);
     }
 
@@ -843,4 +843,41 @@
             return rule;
         }
     }
+
+    /** @hide */
+    public static final class AzrWithId implements Parcelable {
+        public final String mId;
+        public final AutomaticZenRule mRule;
+
+        public AzrWithId(String id, AutomaticZenRule rule) {
+            mId = id;
+            mRule = rule;
+        }
+
+        public static final Creator<AzrWithId> CREATOR = new Creator<>() {
+            @Override
+            public AzrWithId createFromParcel(Parcel in) {
+                return new AzrWithId(
+                        in.readString8(),
+                        in.readParcelable(AutomaticZenRule.class.getClassLoader(),
+                                AutomaticZenRule.class));
+            }
+
+            @Override
+            public AzrWithId[] newArray(int size) {
+                return new AzrWithId[size];
+            }
+        };
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString8(mId);
+            dest.writeParcelable(mRule, flags);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 00df724..1f0cd39 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -224,7 +224,7 @@
     void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
     ZenPolicy getDefaultZenPolicy();
     AutomaticZenRule getAutomaticZenRule(String id);
-    Map<String, AutomaticZenRule> getAutomaticZenRules();
+    ParceledListSlice getAutomaticZenRules();
     String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg, boolean fromUser);
     boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule, boolean fromUser);
     boolean removeAutomaticZenRule(String id, boolean fromUser);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8af5b1b..19fecb9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -101,14 +101,20 @@
      */
     public static final String REPORT_KEY_STREAMRESULT = "stream";
 
-    static final String TAG = "Instrumentation";
+    /**
+     * @hide
+     */
+    public static final String TAG = "Instrumentation";
 
     private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
 
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
-    // If set, will print the stack trace for activity starts within the process
-    static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+    /**
+     * If set, will print the stack trace for activity starts within the process
+     * @hide
+     */
+    public static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
     static final boolean DEBUG_FINISH_ACTIVITY = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean("persist.wm.debug.finish_activity", false);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 050ef23..69e3ef9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1747,7 +1747,15 @@
     public Map<String, AutomaticZenRule> getAutomaticZenRules() {
         INotificationManager service = service();
         try {
-            return service.getAutomaticZenRules();
+            Map<String, AutomaticZenRule> result = new HashMap<>();
+            ParceledListSlice<AutomaticZenRule.AzrWithId> parceledRules =
+                    service.getAutomaticZenRules();
+            if (parceledRules != null) {
+                for (AutomaticZenRule.AzrWithId rule : parceledRules.getList()) {
+                    result.put(rule.mId, rule.mRule);
+                }
+            }
+            return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index afe915e..dd87d28 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -594,7 +594,7 @@
      * @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
      */
     public boolean isSeamlessResizeEnabled() {
-        return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
+        return mSeamlessResizeEnabled == null ? false : mSeamlessResizeEnabled;
     }
 
     /**
diff --git a/core/java/android/app/admin/StringSetIntersection.java b/core/java/android/app/admin/StringSetIntersection.java
new file mode 100644
index 0000000..5f2031e
--- /dev/null
+++ b/core/java/android/app/admin/StringSetIntersection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Set;
+
+/**
+ * Class to identify a intersection resolution mechanism for {@code Set<String>} policies, it's
+ * used to resolve the enforced policy when being set by multiple admins (see {@link
+ * PolicyState#getResolutionMechanism()}).
+ *
+ * @hide
+ */
+public final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+    /**
+     * Intersection resolution for policies represented {@code Set<String>} which resolves as the
+     * intersection of all sets.
+     */
+    @NonNull
+    public static final StringSetIntersection STRING_SET_INTERSECTION = new StringSetIntersection();
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        return o != null && getClass() == o.getClass();
+    }
+
+    @Override
+    public int hashCode() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "StringSetIntersection {}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+    @NonNull
+    public static final Parcelable.Creator<StringSetIntersection> CREATOR =
+            new Parcelable.Creator<StringSetIntersection>() {
+                @Override
+                public StringSetIntersection createFromParcel(Parcel source) {
+                    return new StringSetIntersection();
+                }
+
+                @Override
+                public StringSetIntersection[] newArray(int size) {
+                    return new StringSetIntersection[size];
+                }
+            };
+}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 572bffe..b87ef70 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -412,3 +412,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "use_policy_intersection_for_permitted_input_methods"
+  namespace: "enterprise"
+  description: "When deciding on permitted input methods, use policy intersection instead of last recorded policy."
+  bug: "340914586"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index a13af7f..d7d6262 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -168,6 +168,12 @@
         return mSampleSize;
     }
 
+    @Override
+    public String toString() {
+        String component = (mComponent != null) ? mComponent.toString() : "{null}";
+        return  component + ":" + mId;
+    }
+
     ////// Comparison overrides
 
     @Override
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49fd634..53966b8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5964,7 +5964,39 @@
      *
      * @see #getLaunchIntentSenderForPackage(String)
      */
-    public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+     public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
+     * Returns a "good" intent to launch a front-door activity in a package.
+     * This is used, for example, to implement an "open" button when browsing
+     * through packages.  The current implementation looks first for a main
+     * activity in the category {@link Intent#CATEGORY_INFO}, and next for a
+     * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
+     * <code>null</code> if neither are found.
+     *
+     * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+     * the caller is not allowed to query for the <code>packageName</code>.
+     *
+     * @param packageName The name of the package to inspect.
+     * @param includeDirectBootUnaware When {@code true}, activities that are direct-boot-unaware
+     *    will be considered even if the device hasn't been unlocked (i.e. querying will be done
+     *    with {@code MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE}).
+     *
+     * @return A fully-qualified {@link Intent} that can be used to launch the
+     * main activity in the package. Returns <code>null</code> if the package
+     * does not contain such an activity, or if <em>packageName</em> is not
+     * recognized.
+     *
+     * @see #getLaunchIntentSenderForPackage(String)
+     *
+     * @hide
+     */
+    public @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName,
+            boolean includeDirectBootUnaware) {
+        throw new UnsupportedOperationException(
+                "getLaunchIntentForPackage(packageName, includeDirectBootUnaware) not implemented"
+                        + " in subclass");
+    }
 
     /**
      * Return a "good" intent to launch a front-door Leanback activity in a
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6cb49b3..4a99285 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,6 +28,7 @@
 import android.annotation.TestApi;
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Flags;
 import android.ravenwood.annotation.RavenwoodClassLoadHook;
 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.ravenwood.annotation.RavenwoodReplace;
@@ -837,9 +839,8 @@
      * @param buffer The ByteBuffer to write the data to.
      * @throws ReadOnlyBufferException if the buffer is read-only.
      * @throws BufferOverflowException if the buffer is too small.
-     *
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
     public final void marshall(@NonNull ByteBuffer buffer) {
         if (buffer == null) {
             throw new NullPointerException();
@@ -875,9 +876,8 @@
      * Fills the raw bytes of this Parcel with data from the supplied buffer.
      *
      * @param buffer will read buffer.remaining() bytes from the buffer.
-     *
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
     public final void unmarshall(@NonNull ByteBuffer buffer) {
         if (buffer == null) {
             throw new NullPointerException();
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 86acb2b..0150d17 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -354,6 +354,15 @@
 
 flag {
      namespace: "system_performance"
+     name: "parcel_marshall_bytebuffer"
+     is_exported: true
+     description: "Parcel marshal/unmarshall APIs that use ByteBuffer."
+     is_fixed_read_only: true
+     bug: "401362825"
+}
+
+flag {
+     namespace: "system_performance"
      name: "perfetto_sdk_tracing"
      description: "Tracing using Perfetto SDK."
      bug: "303199244"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 4cbd5be..fce2df1 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -62,6 +62,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Build;
@@ -460,14 +461,21 @@
     }
 
     private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) {
-        final int len = source.readInt();
+        int len = source.readInt();
         if (len > 0) {
             final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
-            source.readStringArray(ids);
-            source.readTypedArray(rules, ZenRule.CREATOR);
+            source.readString8Array(ids);
+            ParceledListSlice<?> parceledRules = source.readParcelable(
+                    ZenRule.class.getClassLoader(), ParceledListSlice.class);
+            List<?> rules = parceledRules != null ? parceledRules.getList() : new ArrayList<>();
+            if (rules.size() != len) {
+                Slog.wtf(TAG, String.format(
+                        "Unexpected parceled rules count (%s != %s), throwing them out",
+                        rules.size(), len));
+                len = 0;
+            }
             for (int i = 0; i < len; i++) {
-                ruleMap.put(ids[i], rules[i]);
+                ruleMap.put(ids[i], (ZenRule) rules.get(i));
             }
         }
     }
@@ -485,8 +493,8 @@
         }
         dest.writeInt(user);
         dest.writeParcelable(manualRule, 0);
-        writeRulesToParcel(automaticRules, dest);
-        writeRulesToParcel(deletedRules, dest);
+        writeRulesToParcel(automaticRules, dest, flags);
+        writeRulesToParcel(deletedRules, dest, flags);
         if (!Flags.modesUi()) {
             dest.writeInt(allowAlarms ? 1 : 0);
             dest.writeInt(allowMedia ? 1 : 0);
@@ -501,18 +509,19 @@
         }
     }
 
-    private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) {
+    private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest,
+            int flags) {
         if (!ruleMap.isEmpty()) {
             final int len = ruleMap.size();
             final String[] ids = new String[len];
-            final ZenRule[] rules = new ZenRule[len];
+            final ArrayList<ZenRule> rules = new ArrayList<>();
             for (int i = 0; i < len; i++) {
                 ids[i] = ruleMap.keyAt(i);
-                rules[i] = ruleMap.valueAt(i);
+                rules.add(ruleMap.valueAt(i));
             }
             dest.writeInt(len);
-            dest.writeStringArray(ids);
-            dest.writeTypedArray(rules, 0);
+            dest.writeString8Array(ids);
+            dest.writeParcelable(new ParceledListSlice<>(rules), flags);
         } else {
             dest.writeInt(0);
         }
@@ -2636,7 +2645,7 @@
             enabled = source.readInt() == 1;
             snoozing = source.readInt() == 1;
             if (source.readInt() == 1) {
-                name = source.readString();
+                name = source.readString8();
             }
             zenMode = source.readInt();
             conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2653,18 @@
             component = source.readParcelable(null, android.content.ComponentName.class);
             configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
             if (source.readInt() == 1) {
-                id = source.readString();
+                id = source.readString8();
             }
             creationTime = source.readLong();
             if (source.readInt() == 1) {
-                enabler = source.readString();
+                enabler = source.readString8();
             }
             zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
             zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
-            pkg = source.readString();
+            pkg = source.readString8();
             allowManualInvocation = source.readBoolean();
-            iconResName = source.readString();
-            triggerDescription = source.readString();
+            iconResName = source.readString8();
+            triggerDescription = source.readString8();
             type = source.readInt();
             userModifiedFields = source.readInt();
             zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2712,7 @@
             dest.writeInt(snoozing ? 1 : 0);
             if (name != null) {
                 dest.writeInt(1);
-                dest.writeString(name);
+                dest.writeString8(name);
             } else {
                 dest.writeInt(0);
             }
@@ -2714,23 +2723,23 @@
             dest.writeParcelable(configurationActivity, 0);
             if (id != null) {
                 dest.writeInt(1);
-                dest.writeString(id);
+                dest.writeString8(id);
             } else {
                 dest.writeInt(0);
             }
             dest.writeLong(creationTime);
             if (enabler != null) {
                 dest.writeInt(1);
-                dest.writeString(enabler);
+                dest.writeString8(enabler);
             } else {
                 dest.writeInt(0);
             }
             dest.writeParcelable(zenPolicy, 0);
             dest.writeParcelable(zenDeviceEffects, 0);
-            dest.writeString(pkg);
+            dest.writeString8(pkg);
             dest.writeBoolean(allowManualInvocation);
-            dest.writeString(iconResName);
-            dest.writeString(triggerDescription);
+            dest.writeString8(iconResName);
+            dest.writeString8(triggerDescription);
             dest.writeInt(type);
             dest.writeInt(userModifiedFields);
             dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 2ed9c3a..8f7f941 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import static android.app.Instrumentation.DEBUG_START_ACTIVITY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -32,6 +33,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
+import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.WindowConfiguration;
 import android.app.WindowConfiguration.WindowingMode;
@@ -45,6 +47,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.InsetsFrameProvider;
 import android.view.InsetsSource;
 import android.view.SurfaceControl;
@@ -642,6 +645,10 @@
      */
     @NonNull
     public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.startTask: taskId=" + taskId
+                    + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
         return this;
     }
@@ -655,11 +662,15 @@
      */
     @NonNull
     public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
-            @Nullable Intent intent, @Nullable Bundle options) {
+            @Nullable Intent fillInIntent, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.sendPendingIntent: sender=" + sender.getIntent()
+                    + " fillInIntent=" + fillInIntent + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
                 .setLaunchOptions(options)
                 .setPendingIntent(sender)
-                .setActivityIntent(intent)
+                .setActivityIntent(fillInIntent)
                 .build());
         return this;
     }
@@ -674,6 +685,10 @@
     @NonNull
     public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
             @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+        if (DEBUG_START_ACTIVITY) {
+            Log.d(Instrumentation.TAG, "WCT.startShortcut: shortcutInfo=" + shortcutInfo
+                    + " options=" + options, new Throwable());
+        }
         mHierarchyOps.add(HierarchyOp.createForStartShortcut(
                 callingPackage, shortcutInfo, options));
         return this;
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 62d89f6..146b386 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -16,19 +16,37 @@
 
 package android.app;
 
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_INFO;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.os.storage.VolumeInfo.STATE_MOUNTED;
 import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -44,6 +62,7 @@
 
 import junit.framework.TestCase;
 
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mockito;
 import org.xmlpull.v1.XmlPullParser;
 
@@ -102,14 +121,14 @@
         sVolumes.add(sPrivateUnmountedVol);
     }
 
-    private static final class MockedApplicationPackageManager extends ApplicationPackageManager {
+    public static class MockedApplicationPackageManager extends ApplicationPackageManager {
         private boolean mForceAllowOnExternal = false;
         private boolean mAllow3rdPartyOnInternal = true;
         private HashMap<ApplicationInfo, Resources> mResourcesMap;
 
         public MockedApplicationPackageManager() {
             super(null, null);
-            mResourcesMap = new HashMap<ApplicationInfo, Resources>();
+            mResourcesMap = new HashMap<>();
         }
 
         public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
@@ -153,7 +172,7 @@
     }
 
     private StorageManager getMockedStorageManager() {
-        StorageManager storageManager = Mockito.mock(StorageManager.class);
+        StorageManager storageManager = mock(StorageManager.class);
         Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes);
         Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL))
                 .thenReturn(sInternalVol);
@@ -190,7 +209,7 @@
         sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
 
         StorageManager storageManager = getMockedStorageManager();
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
 
@@ -220,7 +239,7 @@
         ApplicationInfo appInfo = new ApplicationInfo();
         StorageManager storageManager = getMockedStorageManager();
 
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
         Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -249,7 +268,7 @@
         ApplicationInfo appInfo = new ApplicationInfo();
         StorageManager storageManager = getMockedStorageManager();
 
-        IPackageManager pm = Mockito.mock(IPackageManager.class);
+        IPackageManager pm = mock(IPackageManager.class);
 
         MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
         appPkgMgr.setForceAllowOnExternal(true);
@@ -291,15 +310,15 @@
 
     public void testExtractPackageItemInfoAttributes_noMetaData() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
                 new int[]{})).isNull();
     }
 
     public void testExtractPackageItemInfoAttributes_noParser() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
                 new int[]{})).isNull();
@@ -307,8 +326,8 @@
 
     public void testExtractPackageItemInfoAttributes_noMetaDataXml() {
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         when(packageItemInfo.loadXmlMetaData(any(), any())).thenReturn(null);
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
@@ -318,9 +337,9 @@
     public void testExtractPackageItemInfoAttributes_nonMatchingRootTag() throws Exception {
         final String rootTag = "rootTag";
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
-        final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        final XmlResourceParser parser = mock(XmlResourceParser.class);
 
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         packageItemInfo.metaData = new Bundle();
@@ -334,11 +353,11 @@
     public void testExtractPackageItemInfoAttributes_successfulExtraction() throws Exception {
         final String rootTag = "rootTag";
         final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
-        final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
-        final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
-        final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
-        final Resources resources = Mockito.mock(Resources.class);
-        final TypedArray attributes = Mockito.mock(TypedArray.class);
+        final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+        final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        final XmlResourceParser parser = mock(XmlResourceParser.class);
+        final Resources resources = mock(Resources.class);
+        final TypedArray attributes = mock(TypedArray.class);
 
         when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
         packageItemInfo.metaData = new Bundle();
@@ -351,4 +370,123 @@
         assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, rootTag,
                 new int[]{})).isEqualTo(attributes);
     }
+
+    public void testGetLaunchIntentForPackage_categoryInfoActivity_returnsIt() throws Exception {
+        String pkg = "com.some.package";
+        int userId = 42;
+        ResolveInfo categoryInfoResolveInfo = new ResolveInfo();
+        categoryInfoResolveInfo.activityInfo = new ActivityInfo();
+        categoryInfoResolveInfo.activityInfo.packageName = pkg;
+        categoryInfoResolveInfo.activityInfo.name = "activity";
+        Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of(categoryInfoResolveInfo))
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+        doReturn(
+                List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNotNull();
+        assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity"));
+        assertThat(intent.getCategories()).containsExactly(CATEGORY_INFO);
+        assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+        verify(pm).queryIntentActivitiesAsUser(
+                eqIntent(new Intent(ACTION_MAIN).addCategory(CATEGORY_INFO).setPackage(pkg)),
+                eqResolveInfoFlags(MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE),
+                eq(userId));
+    }
+
+    public void testGetLaunchIntentForPackage_categoryLauncherActivity_returnsIt() {
+        String pkg = "com.some.package";
+        int userId = 42;
+        ResolveInfo categoryLauncherResolveInfo1 = new ResolveInfo();
+        categoryLauncherResolveInfo1.activityInfo = new ActivityInfo();
+        categoryLauncherResolveInfo1.activityInfo.packageName = pkg;
+        categoryLauncherResolveInfo1.activityInfo.name = "activity1";
+        ResolveInfo categoryLauncherResolveInfo2 = new ResolveInfo();
+        categoryLauncherResolveInfo2.activityInfo = new ActivityInfo();
+        categoryLauncherResolveInfo2.activityInfo.packageName = pkg;
+        categoryLauncherResolveInfo2.activityInfo.name = "activity2";
+        Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+        doReturn(
+                List.of(categoryLauncherResolveInfo1, categoryLauncherResolveInfo2))
+                .when(pm).queryIntentActivitiesAsUser(
+                        eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNotNull();
+        assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity1"));
+        assertThat(intent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+        assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    public void testGetLaunchIntentForPackage_noSuitableActivity_returnsNull() throws Exception {
+        String pkg = "com.some.package";
+        int userId = 42;
+
+        final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+        doReturn(userId).when(pm).getUserId();
+        doReturn(List.of())
+                .when(pm).queryIntentActivitiesAsUser(
+                        any(),
+                        any(ResolveInfoFlags.class),
+                        anyInt());
+
+        Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+        assertThat(intent).isNull();
+    }
+
+    /** Equality check for intents -- ignoring extras */
+    private static Intent eqIntent(Intent wanted) {
+        return argThat(
+                new ArgumentMatcher<>() {
+                    @Override
+                    public boolean matches(Intent argument) {
+                        return wanted.filterEquals(argument)
+                                && wanted.getFlags() == argument.getFlags();
+                    }
+
+                    @Override
+                    public String toString() {
+                        return wanted.toString();
+                    }
+                });
+    }
+
+    private static ResolveInfoFlags eqResolveInfoFlags(long flagsWanted) {
+        return argThat(
+                new ArgumentMatcher<>() {
+                    @Override
+                    public boolean matches(ResolveInfoFlags argument) {
+                        return argument.getValue() == flagsWanted;
+                    }
+
+                    @Override
+                    public String toString() {
+                        return String.valueOf(flagsWanted);
+                    }
+                });
+    }
 }
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index bb05910..3e652010 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,8 +29,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -418,63 +416,4 @@
         int binderEndPos = pA.dataPosition();
         assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
     }
-
-    private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
-
-    // Allow for some Parcel overhead
-    private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
-
-    @Test
-    public void testMarshall_ByteBuffer_wrapped() {
-        ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
-        testMarshall_ByteBuffer(bb);
-    }
-
-    @Test
-    public void testMarshall_DirectByteBuffer() {
-        ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
-        testMarshall_ByteBuffer(bb);
-    }
-
-    private void testMarshall_ByteBuffer(ByteBuffer bb) {
-        // Ensure that Parcel respects the starting offset by not starting at 0
-        bb.position(1);
-        bb.mark();
-
-        // Parcel test data, then marshall into the ByteBuffer
-        Parcel p1 = Parcel.obtain();
-        p1.writeByteArray(TEST_DATA);
-        p1.marshall(bb);
-        p1.recycle();
-
-        assertTrue(bb.position() > 1);
-        bb.reset();
-
-        // Unmarshall test data into a new Parcel
-        Parcel p2 = Parcel.obtain();
-        bb.reset();
-        p2.unmarshall(bb);
-        assertTrue(bb.position() > 1);
-        p2.setDataPosition(0);
-        byte[] marshalled = p2.marshall();
-
-        bb.reset();
-        for (int i = 0; i < TEST_DATA.length; i++) {
-            assertEquals(bb.get(), marshalled[i]);
-        }
-
-        byte[] testDataCopy = new byte[TEST_DATA.length];
-        p2.setDataPosition(0);
-        p2.readByteArray(testDataCopy);
-        for (int i = 0; i < TEST_DATA.length; i++) {
-            assertEquals(TEST_DATA[i], testDataCopy[i]);
-        }
-
-        // Test that overflowing the buffer throws an exception
-        bb.reset();
-        // Leave certainly not enough room for the test data
-        bb.limit(bb.position() + TEST_DATA.length - 1);
-        assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
-        p2.recycle();
-    }
 }
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index 9bb51a8..ef30d89 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -34,6 +34,7 @@
         android:src="@drawable/bubble_ic_settings"/>
 
     <TextView
+        android:id="@+id/education_manage_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
         android:text="@string/bubble_bar_education_manage_title"/>
 
     <TextView
+        android:id="@+id/education_manage_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index 1616707..9076d6a 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -34,6 +34,7 @@
         android:src="@drawable/ic_floating_landscape"/>
 
     <TextView
+        android:id="@+id/education_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
         android:text="@string/bubble_bar_education_stack_title"/>
 
     <TextView
+        android:id="@+id/education_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 225303b..17ebac9 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -40,6 +40,7 @@
             android:tint="@color/bubbles_icon_tint"/>
 
         <TextView
+            android:id="@+id/manage_dismiss"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
@@ -67,6 +68,7 @@
             android:tint="@color/bubbles_icon_tint"/>
 
         <TextView
+            android:id="@+id/manage_dont_bubble"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 426c3ee..290ef16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -27,6 +27,7 @@
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.TypefaceUtils.setTypeface;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -71,6 +72,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.AlphaOptimizedButton;
 import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
 import com.android.wm.shell.taskview.TaskView;
 
@@ -551,6 +553,7 @@
         mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
                 R.layout.bubble_manage_button, this /* parent */, false /* attach */);
         addView(mManageButton);
+        setTypeface(mManageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
         mManageButton.setVisibility(visibility);
         setManageClickListener();
         post(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index da6948d..92007a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -50,6 +50,7 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 /**
  * Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
@@ -165,8 +166,10 @@
         LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
         mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
         mSenderText = findViewById(R.id.bubble_flyout_name);
+        TypefaceUtils.setTypeface(mSenderText, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
         mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
         mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+        TypefaceUtils.setTypeface(mMessageText, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
 
         final Resources res = getResources();
         mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 64f54b8..e901e0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -46,6 +46,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -234,6 +235,10 @@
         setBackgroundColor(bgColor);
         mEmptyStateTitle.setTextColor(textColor);
         mEmptyStateSubtitle.setTextColor(textColor);
+        TypefaceUtils.setTypeface(mEmptyStateTitle,
+                TypefaceUtils.FontFamily.GSF_BODY_MEDIUM_EMPHASIZED);
+        TypefaceUtils.setTypeface(mEmptyStateSubtitle, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
+
     }
 
     public void updateFontSize() {
@@ -322,6 +327,7 @@
 
         TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
         viewName.setTextColor(textColor);
+        TypefaceUtils.setTypeface(viewName, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
 
         return new ViewHolder(overflowView, mPositioner);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index dd5a23a..3dce456 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -89,6 +89,8 @@
 import com.android.wm.shell.bubbles.animation.StackAnimationController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
 import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
@@ -1397,6 +1399,14 @@
         // The menu itself should respect locale direction so the icons are on the correct side.
         mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
         addView(mManageMenu);
+
+        // Doesn't seem to work unless view is added; so set font after.
+        TypefaceUtils.setTypeface(findViewById(R.id.manage_dismiss), FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(findViewById(R.id.manage_dont_bubble),
+                FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(mManageSettingsText, FontFamily.GSF_LABEL_LARGE);
+        TypefaceUtils.setTypeface(findViewById(R.id.bubble_manage_menu_fullscreen_title),
+                FontFamily.GSF_LABEL_LARGE);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 39a2a7b..d2ad708 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,6 +27,7 @@
 import android.widget.LinearLayout
 import com.android.internal.R.color.system_neutral1_900
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.Interpolators
 
 /**
@@ -53,6 +54,12 @@
 
     init {
         LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
+        TypefaceUtils.setTypeface(findViewById(R.id.user_education_title),
+            TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+        TypefaceUtils.setTypeface(findViewById(R.id.user_education_description),
+            TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+        TypefaceUtils.setTypeface(manageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
+        TypefaceUtils.setTypeface(gotItButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 1660619..9ac05989 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,6 +26,7 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.Interpolators
 
 /**
@@ -59,6 +60,9 @@
 
     init {
         LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
+        TypefaceUtils.setTypeface(titleTextView,
+            TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+        TypefaceUtils.setTypeface(descTextView, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
 
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
index 6c14d83..bccc6dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -25,6 +25,7 @@
 import android.widget.TextView;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 /**
  * Bubble bar expanded view menu item view to display menu action details
@@ -55,6 +56,7 @@
         super.onFinishInflate();
         mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
         mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+        TypefaceUtils.setTypeface(mTextView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index dfbf655..7c0f8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -33,6 +33,7 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.TypefaceUtils;
 
 import java.util.ArrayList;
 
@@ -75,6 +76,7 @@
         mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
         mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
         mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+        TypefaceUtils.setTypeface(mBubbleTitleView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
         mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
         updateThemeColors();
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7adec39..0bd3a54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -35,6 +35,7 @@
 import com.android.wm.shell.bubbles.BubbleEducationController
 import com.android.wm.shell.bubbles.BubbleViewProvider
 import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.shared.TypefaceUtils
 import com.android.wm.shell.shared.animation.PhysicsAnimator
 import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
 import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -108,6 +109,10 @@
         root.getBoundsOnScreen(rootBounds)
         educationView =
             createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+                TypefaceUtils.setTypeface(findViewById(R.id.education_title),
+                    TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+                TypefaceUtils.setTypeface(findViewById(R.id.education_text),
+                    TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
                 setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
                 updateEducationPosition(view = this, position, rootBounds)
                 val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
@@ -153,6 +158,10 @@
 
         educationView =
             createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+                TypefaceUtils.setTypeface(findViewById(R.id.education_manage_title),
+                    TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+                TypefaceUtils.setTypeface(findViewById(R.id.education_manage_text),
+                    TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
                 pivotY = 0f
                 doOnLayout { it.pivotX = it.width / 2f }
                 setOnClickListener { hideEducation(animated = true) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index dd5827a..320de2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -142,8 +142,8 @@
 ## Tracing activity starts & finishes in the app process
 
 It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
-(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
-get this trace:
+or via a `WindowContainerTransaction` (ie. if you are repro'ing a bug related to activity starts).
+You can enable this system property to get this trace:
 ```shell
 # Enabling
 adb shell setprop persist.wm.debug.start_activity true
@@ -168,6 +168,21 @@
 adb reboot
 ```
 
+## Tracing transition requests in the Shell
+
+To trace where a new WM transition is started in the Shell, you can enable this system property:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.start_shell_transition true
+adb reboot
+adb logcat -s "ShellTransitions"
+
+# Disabling
+adb shell setprop persist.wm.debug.start_shell_transition \"\"
+adb reboot
+```
+
+
 ## Dumps
 
 Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index cef18f5..c58bb6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -40,7 +40,6 @@
 import android.view.WindowManager;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.NonNull;
@@ -341,23 +340,6 @@
         return false;
     }
 
-    /**
-     * @return a change representing a config-at-end activity for a given parent.
-     */
-    @Nullable
-    public TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
-            @android.annotation.NonNull WindowContainerToken parent) {
-        for (TransitionInfo.Change change : info.getChanges()) {
-            if (change.getTaskInfo() == null
-                    && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
-                    && change.getParent() != null && change.getParent().equals(parent)) {
-                return change;
-            }
-        }
-        return null;
-    }
-
-
     /** Whether a particular package is same as current pip package. */
     public boolean isPackageActiveInPip(@Nullable String packageName) {
         // No-op, to be handled differently in PIP1 and PIP2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index cfcd563..5d8d8b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -76,6 +76,7 @@
 import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
 import com.android.wm.shell.pip2.animation.PipEnterAnimator;
 import com.android.wm.shell.pip2.phone.transition.PipExpandHandler;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -387,8 +388,8 @@
         mFinishCallback = finishCallback;
         // We expect the PiP activity as a separate change in a config-at-end transition;
         // only flings are not using config-at-end for resize bounds changes
-        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
-                pipChange.getTaskInfo().getToken());
+        TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+                info, pipChange.getTaskInfo().getToken());
         if (pipActivityChange != null) {
             // Transform calculations use PiP params by default, so make sure they are null to
             // default to using bounds for scaling calculations instead.
@@ -427,8 +428,8 @@
         }
 
         // We expect the PiP activity as a separate change in a config-at-end transition.
-        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
-                pipChange.getTaskInfo().getToken());
+        TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+                info, pipChange.getTaskInfo().getToken());
         if (pipActivityChange == null) {
             return false;
         }
@@ -497,8 +498,8 @@
         }
 
         // We expect the PiP activity as a separate change in a config-at-end transition.
-        TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
-                pipChange.getTaskInfo().getToken());
+        TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+                info, pipChange.getTaskInfo().getToken());
         if (pipActivityChange == null) {
             return false;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
index 01cda6c..e562f33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
@@ -67,6 +67,36 @@
     }
 
     /**
+     * @return a change representing a config-at-end activity for ancestor.
+     */
+    @Nullable
+    public static TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+            @NonNull WindowContainerToken ancestor) {
+        final TransitionInfo.Change ancestorChange =
+                PipTransitionUtils.getChangeByToken(info, ancestor);
+        if (ancestorChange == null) return null;
+
+        // Iterate through changes bottom-to-top, going up the parent chain starting with ancestor.
+        TransitionInfo.Change lastPipChildChange = ancestorChange;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            TransitionInfo.Change change = info.getChanges().get(i);
+            if (change == ancestorChange) continue;
+
+            if (change.getParent() != null
+                    && change.getParent().equals(lastPipChildChange.getContainer())) {
+                // Found a child of the last cached child along the ancestral chain.
+                lastPipChildChange = change;
+                if (change.getTaskInfo() == null
+                        && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)) {
+                    // If this is a config-at-end activity change, then we found the chain leaf.
+                    return change;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * @return the leash to interact with the container this change represents.
      * @throws NullPointerException if the leash is null.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 1853ffa..320a63a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -34,6 +34,7 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.StageCoordinator;
 import com.android.wm.shell.unfold.UnfoldTransitionHandler;
@@ -132,7 +133,7 @@
 
         TransitionInfo.Change pipActivityChange = null;
         if (pipChange != null) {
-            pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+            pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
                     info, pipChange.getContainer());
             everythingElse.getChanges().remove(pipActivityChange);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e28a7fa..003ef1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -39,6 +39,7 @@
 
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
 import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -52,6 +53,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -138,6 +140,10 @@
         ShellCommandHandler.ShellCommandActionHandler {
     static final String TAG = "ShellTransitions";
 
+    // If set, will print the stack trace for transition starts within the process
+    static final boolean DEBUG_START_TRANSITION = Build.IS_DEBUGGABLE &&
+            SystemProperties.getBoolean("persist.wm.debug.start_shell_transition", false);
+
     /** Set to {@code true} to enable shell transitions. */
     public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
     public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
@@ -346,10 +352,10 @@
         mShellController = shellController;
         // The very last handler (0 in the list) should be the default one.
         mHandlers.add(mDefaultTransitionHandler);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Default");
         // Next lowest priority is remote transitions.
         mHandlers.add(mRemoteTransitionHandler);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Remote");
         shellInit.addInitCallback(this::onInit, this);
         mHomeTransitionObserver = homeTransitionObserver;
         mFocusTransitionObserver = focusTransitionObserver;
@@ -439,7 +445,7 @@
         mHandlers.add(handler);
         // Set initial scale settings.
         handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: %s",
                 handler.getClass().getSimpleName());
     }
 
@@ -691,7 +697,7 @@
     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
         info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
                 info.getDebugId(), transitionToken, info.toString("    " /* prefix */));
         int activeIdx = findByToken(mPendingTransitions, transitionToken);
         if (activeIdx < 0) {
@@ -753,7 +759,7 @@
                 if (tr.isIdle()) continue;
                 hadPreceding = true;
                 // Sleep starts a process of forcing all prior transitions to finish immediately
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                ProtoLog.v(WM_SHELL_TRANSITIONS,
                         "Start finish-for-sync track %d", i);
                 finishForSync(active.mToken, i, null /* forceFinish */);
             }
@@ -797,7 +803,7 @@
         if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) {
             // No root-leashes implies that the transition is empty/no-op, so just do
             // housekeeping and return.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "No transition roots in %s so"
                     + " abort", active);
             onAbort(active);
             return true;
@@ -839,7 +845,7 @@
                 && allOccluded)) {
             // Treat this as an abort since we are bypassing any merge logic and effectively
             // finishing immediately.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+            ProtoLog.v(WM_SHELL_TRANSITIONS,
                     "Non-visible anim so abort: %s", active);
             onAbort(active);
             return true;
@@ -873,7 +879,7 @@
     void processReadyQueue(Track track) {
         if (track.mReadyTransitions.isEmpty()) {
             if (track.mActiveTransition == null) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Track %d became idle",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, "Track %d became idle",
                         mTracks.indexOf(track));
                 if (areTracksIdle()) {
                     if (!mReadyDuringSync.isEmpty()) {
@@ -885,7 +891,7 @@
                             if (!success) break;
                         }
                     } else if (mPendingTransitions.isEmpty()) {
-                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
+                        ProtoLog.v(WM_SHELL_TRANSITIONS, "All active transition "
                                 + "animations finished");
                         mKnownTransitions.clear();
                         // Run all runnables from the run-when-idle queue.
@@ -926,7 +932,7 @@
             onMerged(playingToken, readyToken);
             return;
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition %s ready while"
                 + " %s is still animating. Notify the animating transition"
                 + " in case they can be merged", ready, playing);
         mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
@@ -955,7 +961,7 @@
         }
 
         final Track track = mTracks.get(playing.getTrack());
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
                 merged, playing);
         int readyIdx = 0;
         if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
@@ -996,7 +1002,7 @@
     }
 
     private void playTransition(@NonNull ActiveTransition active) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
         final var token = active.mToken;
 
         for (int i = 0; i < mObservers.size(); ++i) {
@@ -1007,12 +1013,12 @@
 
         // If a handler already chose to run this animation, try delegating to it first.
         if (active.mHandler != null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+            ProtoLog.v(WM_SHELL_TRANSITIONS, " try firstHandler %s",
                     active.mHandler);
             boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
                     active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
             if (consumed) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by firstHandler");
                 mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                     Trace.instant(TRACE_TAG_WINDOW_MANAGER,
@@ -1042,14 +1048,14 @@
     ) {
         for (int i = mHandlers.size() - 1; i >= 0; --i) {
             if (mHandlers.get(i) == skip) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " skip handler %s",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " skip handler %s",
                         mHandlers.get(i));
                 continue;
             }
             boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
                     finishCB);
             if (consumed) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+                ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by %s",
                         mHandlers.get(i));
                 mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
                 if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
@@ -1155,7 +1161,7 @@
         for (int i = 0; i < mObservers.size(); ++i) {
             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition animation finished "
                 + "(aborted=%b), notifying core %s", active.mAborted, active);
         if (active.mStartT != null) {
             // Applied by now, so clear immediately to remove any references. Do not set to null
@@ -1209,7 +1215,7 @@
 
     void requestStartTransition(@NonNull IBinder transitionToken,
             @Nullable TransitionRequestInfo request) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
                 request.getDebugId(), transitionToken, request);
         if (mKnownTransitions.containsKey(transitionToken)) {
             throw new RuntimeException("Transition already started " + transitionToken);
@@ -1228,6 +1234,8 @@
             if (requestResult != null) {
                 active.mHandler = requestResult.first;
                 wct = requestResult.second;
+                ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition (#%d): request handled by %s",
+                        request.getDebugId(), active.mHandler.getClass().getSimpleName());
             }
             if (request.getDisplayChange() != null) {
                 TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
@@ -1273,8 +1281,12 @@
      */
     public IBinder startTransition(@WindowManager.TransitionType int type,
             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+        ProtoLog.v(WM_SHELL_TRANSITIONS, "Directly starting a new transition "
                 + "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
+        if (DEBUG_START_TRANSITION) {
+            Log.d(TAG, "startTransition: type=" + transitTypeToString(type)
+                    + " wct=" + wct + " handler=" + handler.getClass().getName(), new Throwable());
+        }
         final ActiveTransition active =
                 new ActiveTransition(mOrganizer.startNewTransition(type, wct));
         active.mHandler = handler;
@@ -1362,7 +1374,7 @@
             }
             // Attempt to merge a SLEEP info to signal that the playing transition needs to
             // fast-forward.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+            ProtoLog.v(WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
                     + " into %s via a SLEEP proxy", nextSync, playing);
             playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
                     playing.mToken, (wct) -> {});
@@ -1598,7 +1610,7 @@
         public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
                 SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                 throws RemoteException {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
+            ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
                     t.getId());
             mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                     iBinder, transitionInfo, t, finishT));
@@ -1784,8 +1796,9 @@
         pw.println(prefix + TAG);
 
         final String innerPrefix = prefix + "  ";
-        pw.println(prefix + "Handlers:");
-        for (TransitionHandler handler : mHandlers) {
+        pw.println(prefix + "Handlers (ordered by priority):");
+        for (int i = mHandlers.size() - 1; i >= 0; i--) {
+            final TransitionHandler handler = mHandlers.get(i);
             pw.print(innerPrefix);
             pw.print(handler.getClass().getSimpleName());
             pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index dc669a5..aa8cbd1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -175,6 +175,8 @@
     if (stream->isValid()) {
         mOpenMultiPicStream = std::move(stream);
         mSerialContext.reset(new SkSharingSerialContext());
+        // passing the GrDirectContext to the SerialContext allows us to raster/serialize GPU images
+        mSerialContext->setDirectContext(mRenderThread.getGrContext());
         SkSerialProcs procs;
         procs.fImageProc = SkSharingSerialContext::serializeImage;
         procs.fImageCtx = mSerialContext.get();
diff --git a/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
new file mode 100644
index 0000000..6b534aa
--- /dev/null
+++ b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2025 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="34dp"
+    android:height="42dp"
+    android:viewportWidth="34"
+    android:viewportHeight="42">
+    <path
+        android:pathData="M0.856,17.569C0.887,19.083 1.004,20.593 1.206,22.094C2.166,28.584 5.804,35.937 15.774,41.089C16.171,41.293 16.61,41.4 17.056,41.4C17.503,41.4 17.942,41.293 18.339,41.089C28.309,35.936 31.947,28.583 32.907,22.093C33.109,20.593 33.226,19.083 33.256,17.569V8.605C33.257,7.919 33.046,7.25 32.652,6.688C32.259,6.127 31.703,5.7 31.059,5.467L18.191,0.8C17.458,0.534 16.655,0.534 15.922,0.8L3.054,5.467C2.41,5.7 1.854,6.127 1.461,6.688C1.067,7.25 0.856,7.919 0.856,8.605V17.569Z"
+        android:fillColor="#D1C2CB"/>
+    <path
+        android:pathData="M15.067,24.333V10.733H18.933V24.333H15.067ZM15.067,31.267V27.433H18.933V31.267H15.067Z"
+        android:fillColor="@color/settingslib_materialColorSurfaceContainerLowest"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
index 54860d4..deda258 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
@@ -21,6 +21,7 @@
             <enum name="low" value="1"/>
             <enum name="medium" value="2"/>
             <enum name="high" value="3"/>
+            <enum name="off" value="4"/>
         </attr>
         <attr name="buttonLevel" format="enum">
             <enum name="generic" value="0"/>
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
index 19181dd..abc458b 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
@@ -22,4 +22,5 @@
     <color name="settingslib_expressive_color_status_level_medium">#FCBD00</color>
     <!-- static palette red50 -->
     <color name="settingslib_expressive_color_status_level_high">#DB372D</color>
+    <color name="settingslib_expressive_color_status_level_off">#D1C2CB</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
index 1f8cfb5..eda281c 100644
--- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
+++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
@@ -40,7 +40,8 @@
         GENERIC,
         LOW,
         MEDIUM,
-        HIGH
+        HIGH,
+        OFF
     }
     var iconLevel: BannerStatus = BannerStatus.GENERIC
         set(value) {
@@ -87,6 +88,7 @@
         1 -> BannerStatus.LOW
         2 -> BannerStatus.MEDIUM
         3 -> BannerStatus.HIGH
+        4 -> BannerStatus.OFF
         else -> BannerStatus.GENERIC
     }
 
@@ -104,7 +106,10 @@
         }
 
         (holder.findViewById(R.id.status_banner_button) as? MaterialButton)?.apply {
-            setBackgroundColor(getBackgroundColor(buttonLevel))
+            setBackgroundColor(
+                if (buttonLevel == BannerStatus.OFF) getBackgroundColor(BannerStatus.GENERIC)
+                else getBackgroundColor(buttonLevel)
+            )
             text = buttonText
             setOnClickListener(listener)
             visibility = if (listener != null) View.VISIBLE else View.GONE
@@ -143,6 +148,11 @@
                 R.color.settingslib_expressive_color_status_level_high
             )
 
+            BannerStatus.OFF -> ContextCompat.getColor(
+                context,
+                R.color.settingslib_expressive_color_status_level_off
+            )
+
             else -> ContextCompat.getColor(
                 context,
                 com.android.settingslib.widget.theme.R.color.settingslib_materialColorPrimary
@@ -167,6 +177,11 @@
                 R.drawable.settingslib_expressive_icon_status_level_high
             )
 
+            BannerStatus.OFF -> ContextCompat.getDrawable(
+                context,
+                R.drawable.settingslib_expressive_icon_status_level_off
+            )
+
             else -> null
         }
     }
@@ -188,6 +203,7 @@
                 R.drawable.settingslib_expressive_background_level_high
             )
 
+            // GENERIC and OFF are using the same background drawable.
             else -> ContextCompat.getDrawable(
                 context,
                 R.drawable.settingslib_expressive_background_generic
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 1362ffe..86559fd 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,22 +1,6 @@
 {
-  // Curious where your @Scenario tests are running?
-  //
-  // @Ignore: Will not run in any configuration
-  //
-  // @FlakyTest: Tests that don't block pre/postsubmit but are staged to run known failures.
-  //             Tests will run in postsubmit on sysui-e2e-staged suite.
-  //
-  //
-  // @PlatinumTest: Marking your test with this annotation will put your tests in presubmit.
-  //                Please DO NOT annotate new or old tests with @PlatinumTest annotation
-  //                without discussing with mdb:android-platinum
-  //
-  // @Postsubmit: Do not use this annotation for e2e tests. This won't have any affect.
-
-  // For all other e2e tests which are not platinum, they run in sysui-silver suite,that
-  // primarily runs in postsubmit with an exception to e2e test related changes.
-  // If you want to see one shot place to monitor all e2e tests, look for
-  // sysui-e2e-staged suite.
+  // Test mappings for SystemUI unit tests.
+  // For e2e mappings, see go/sysui-e2e-test-mapping
 
   // v2/android-virtual-infra/test_mapping/presubmit-avd
   "presubmit": [
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4693377..7cfd3e7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -2136,3 +2136,14 @@
     description: "Enables return animations for status bar chips"
     bug: "202516970"
 }
+
+flag {
+    name: "media_projection_grey_error_text"
+    namespace: "systemui"
+    description: "Set the error text color to grey when app sharing is hidden by the requesting app"
+    bug: "390624334"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index ca94482..21ec896 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -245,6 +245,17 @@
                 runner.onAnimationCancelled();
                 finishRunnable.run();
             }
+
+            @Override
+            public void onTransitionConsumed(IBinder transition, boolean aborted)
+                    throws RemoteException {
+                // Notify the remote runner that the transition has been canceled if the transition
+                // was merged into another transition or aborted
+                synchronized (mFinishRunnables) {
+                    mFinishRunnables.remove(transition);
+                }
+                runner.onAnimationCancelled();
+            }
         };
     }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 3150e94..2b8fe39 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -47,6 +47,7 @@
 import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
 import com.android.compose.animation.scene.transitions
 import com.android.compose.modifiers.thenIf
+import com.android.systemui.Flags
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -104,7 +105,9 @@
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
             fade(Communal.Elements.LockIcon)
-            fade(Communal.Elements.StatusBar)
+            if (!Flags.glanceableHubV2()) {
+                fade(Communal.Elements.StatusBar)
+            }
         }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
@@ -131,7 +134,9 @@
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
             fade(Communal.Elements.LockIcon)
-            fade(Communal.Elements.StatusBar)
+            if (!Flags.glanceableHubV2()) {
+                fade(Communal.Elements.StatusBar)
+            }
         }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2d03e2b..0181928 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import com.android.compose.animation.scene.ContentScope
+import com.android.systemui.Flags
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
 import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
@@ -70,8 +71,10 @@
                 content = {
                     Box(modifier = Modifier.fillMaxSize()) {
                         with(communalPopupSection) { Popup() }
-                        with(ambientStatusBarSection) {
-                            AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+                        if (!Flags.glanceableHubV2()) {
+                            with(ambientStatusBarSection) {
+                                AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+                            }
                         }
                         CommunalHub(
                             viewModel = viewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 0f63150..d9990ba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -275,14 +275,14 @@
         setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
                 "abc/.def", /* validateActivity */ true, /* enableSetting */true,
                 /* enableOnLockScreen */ true);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
         // Once from setup + twice from this function
         verify(mCallback, times(3)).onQRCodeScannerPreferenceChanged();
@@ -297,14 +297,14 @@
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
                 UserHandle.USER_CURRENT);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
         assertThat(mController.isAllowedOnLockScreen()).isTrue();
         assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
 
-        mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+        mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
                 UserHandle.USER_CURRENT);
         verifyActivityDetails("abc/.def");
         assertThat(mController.isEnabledForLockScreenButton()).isTrue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index 8efc031..12ade62 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -365,6 +365,22 @@
 
     @Test
     @EnableFlags(NotificationBundleUi.FLAG_NAME)
+    fun onDragSuccess() {
+        val notification: Notification =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .addAction(mock(Notification.Action::class.java))
+                .build()
+        val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+        underTest = factory.create(entry) as NotificationEntryAdapter
+
+        underTest.onDragSuccess()
+        verify(kosmos.mockNotificationActivityStarter).onDragSuccess(entry)
+    }
+
+    @Test
+    @EnableFlags(NotificationBundleUi.FLAG_NAME)
     fun onNotificationBubbleIconClicked() {
         val notification: Notification =
             Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 46430af..1f37291 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -790,6 +790,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
     fun animateToGlanceableHub_affectsAlpha() =
         testScope.runTest {
             try {
@@ -809,6 +810,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
     fun animateToGlanceableHub_alphaResetOnCommunalNotShowing() =
         testScope.runTest {
             try {
diff --git a/packages/SystemUI/res/color/brightness_slider_track.xml b/packages/SystemUI/res/color/brightness_slider_track.xml
new file mode 100644
index 0000000..c6e9b65
--- /dev/null
+++ b/packages/SystemUI/res/color/brightness_slider_track.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral2_500" android:lStar="40" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 88d3ecb..d38da7b 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -25,7 +25,7 @@
             <shape>
                 <size android:height="@dimen/rounded_slider_track_width" />
                 <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
-                <solid android:color="@androidprv:color/customColorShadeInactive" />
+                <solid android:color="@color/brightness_slider_track" />
             </shape>
         </inset>
     </item>
diff --git a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
index 73704f8..f17cc96 100644
--- a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
+++ b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
@@ -21,7 +21,7 @@
         android:height="2dp"
         android:width="@dimen/rear_display_progress_width">
         <shape android:shape="rectangle">
-            <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
+            <solid android:color="?android:attr/colorAccent"/>
         </shape>
     </item>
     <item
@@ -29,4 +29,4 @@
         android:gravity="center_vertical|fill_horizontal">
         <com.android.systemui.util.RoundedCornerProgressDrawable android:drawable="@drawable/rear_display_dialog_seekbar_progress" />
     </item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fdb98b..924eaa4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1483,6 +1483,8 @@
     <string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
     <!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+    <!-- Explanation that the app requesting the projection does not support single app sharing[CHAR LIMIT=70] -->
+    <string name="media_projection_entry_app_permission_dialog_single_app_not_supported">Not supported by the app</string>
     <!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
     <string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 0b116de..438dff9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -139,7 +139,7 @@
     fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
         repository.maxAlpha.value =
             if (supportsAmbientMode) {
-                0.7f
+                0.54f
             } else {
                 1f
             }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index c6e4db7..324a3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -150,6 +150,13 @@
             titleTextView.isEnabled = true
         } else {
             errorTextView.visibility = View.VISIBLE
+            if (com.android.systemui.Flags.mediaProjectionGreyErrorText()) {
+                errorTextView.isEnabled = false
+                errorTextView.setTextColor(context.getColorStateList(R.color.menu_item_text))
+                errorTextView.setText(
+                    R.string.media_projection_entry_app_permission_dialog_single_app_not_supported
+                )
+            }
             titleTextView.isEnabled = false
         }
         return view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index fcdcc3f..3bb1ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -23,6 +23,7 @@
 
 import com.android.systemui.DejankUtils;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -44,13 +45,20 @@
     private final Optional<Bubbles> mBubblesOptional;
     private final NotificationActivityStarter mNotificationActivityStarter;
 
-    private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
-            new ExpandableNotificationRow.OnDragSuccessListener() {
-                @Override
-                public void onDragSuccess(NotificationEntry entry) {
-                    mNotificationActivityStarter.onDragSuccess(entry);
-                }
-            };
+    private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener
+            = new ExpandableNotificationRow.OnDragSuccessListener() {
+        @Override
+        public void onDragSuccess(NotificationEntry entry) {
+            NotificationBundleUi.assertInLegacyMode();
+            mNotificationActivityStarter.onDragSuccess(entry);
+        }
+
+        @Override
+        public void onDragSuccess(EntryAdapter entryAdapter) {
+            NotificationBundleUi.isUnexpectedlyInLegacyMode();
+            entryAdapter.onDragSuccess();
+        }
+    };
 
     private NotificationClicker(
             NotificationClickerLogger logger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index 2967c65..e743d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -126,6 +126,11 @@
         return false
     }
 
+    override fun onDragSuccess() {
+        // do nothing. these should not be draggable
+        Log.wtf(TAG, "onDragSuccess() called")
+    }
+
     override fun onNotificationBubbleIconClicked() {
         // do nothing. these cannot be a bubble
         Log.wtf(TAG, "onNotificationBubbleIconClicked() called")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index dbb5c10..f39bd03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -144,6 +144,8 @@
         return false;
     }
 
+    void onDragSuccess();
+
     /**
      * Process a click on a notification bubble icon
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index bda28a9..12cfa91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -149,6 +149,10 @@
         return entry.sbn.notification.fullScreenIntent != null
     }
 
+    override fun onDragSuccess() {
+        notificationActivityStarter.onDragSuccess(entry)
+    }
+
     override fun onNotificationBubbleIconClicked() {
         notificationActivityStarter.onNotificationBubbleIconClicked(entry)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0661b1c..2a3b266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -22,9 +22,9 @@
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_EXPANDED;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
+import static com.android.systemui.Flags.notificationRowAccessibilityExpanded;
 import static com.android.systemui.Flags.notificationRowTransparency;
 import static com.android.systemui.Flags.notificationsPinnedHunInShade;
-import static com.android.systemui.Flags.notificationRowAccessibilityExpanded;
 import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
@@ -2520,7 +2520,11 @@
      */
     public void dragAndDropSuccess() {
         if (mOnDragSuccessListener != null) {
-            mOnDragSuccessListener.onDragSuccess(getEntry());
+            if (NotificationBundleUi.isEnabled()) {
+                mOnDragSuccessListener.onDragSuccess(getEntryAdapter());
+            } else {
+                mOnDragSuccessListener.onDragSuccess(getEntryLegacy());
+            }
         }
     }
 
@@ -4422,6 +4426,12 @@
          * @param entry NotificationEntry that succeed to drop on proper target window.
          */
         void onDragSuccess(NotificationEntry entry);
+
+        /**
+         * @param entryAdapter The EntryAdapter that successfully dropped on the proper
+         *            target window
+         */
+        void onDragSuccess(EntryAdapter entryAdapter);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
index 08c1d71..03990bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -87,9 +87,15 @@
                 // It's not a system app at all.
                 return false
             } else {
-                // If there's no launch intent, it's probably a headless app.
-                val pm = context.packageManager
-                return (pm.getLaunchIntentForPackage(info.packageName) == null)
+                // If there's no launch intent, it's probably a headless app. Check for both
+                // direct-aware and -unaware intents; otherwise this will almost certainly fail
+                // for notifications posted before unlocking.
+                val packageLaunchIntent =
+                    context.packageManager.getLaunchIntentForPackage(
+                        info.packageName,
+                        /* includeDirectBootUnaware= */ true,
+                    )
+                return packageLaunchIntent == null
             }
         } else {
             // If for some reason we don't have the app info, we don't know; best assume it's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 36193bd..3c14462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -49,6 +49,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.Flags;
 import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -475,12 +476,14 @@
                 UserHandle.USER_ALL);
         updateUserSwitcher();
         onThemeChanged();
-        collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
-                mCoroutineDispatcher);
-        collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
-                mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
-        collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
-                mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+        if (!Flags.glanceableHubV2()) {
+            collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
+                    mCoroutineDispatcher);
+            collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
+                    mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+            collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
+                    mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+        }
         if (NewStatusBarIcons.isEnabled()) {
             ComposeView batteryComposeView = new ComposeView(mContext);
             UnifiedBatteryViewBinder.bind(
@@ -645,7 +648,7 @@
                         && !mDozing
                         && !hideForBypass
                         && !mDisableStateTracker.isDisabled()
-                        && (!mCommunalShowing || mExplicitAlpha != -1)
+                        && (Flags.glanceableHubV2() || (!mCommunalShowing || mExplicitAlpha != -1))
                         ? View.VISIBLE : View.INVISIBLE;
 
         updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
index 1a8ca95..f4afc24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.kairos.BuildSpec
 import com.android.systemui.kairos.ExperimentalKairosApi
 import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
 import com.android.systemui.kairos.flatMap
 import com.android.systemui.kairosBuilder
 import com.android.systemui.log.table.TableLogBuffer
@@ -55,9 +56,15 @@
     @Assisted private val isCarrierMerged: State<Boolean>,
 ) : MobileConnectionRepositoryKairos, KairosBuilder by kairosBuilder() {
 
+    private var dumpCache: DumpCache? = null
+
     init {
         onActivated {
             logDiffsForTable(isCarrierMerged, tableLogBuffer, columnName = "isCarrierMerged")
+            combine(isCarrierMerged, activeRepo) { isCarrierMerged, activeRepo ->
+                    DumpCache(isCarrierMerged, activeRepo)
+                }
+                .observe { dumpCache = it }
         }
     }
 
@@ -198,13 +205,6 @@
 
     override val isInEcmMode: State<Boolean> = activeRepo.flatMap { it.isInEcmMode }
 
-    private var dumpCache: DumpCache? = null
-
-    private data class DumpCache(
-        val isCarrierMerged: Boolean,
-        val activeRepo: MobileConnectionRepositoryKairos,
-    )
-
     fun dump(pw: PrintWriter) {
         val cache = dumpCache ?: return
         val ipw = IndentingPrintWriter(pw, "  ")
@@ -227,6 +227,11 @@
         ipw.decreaseIndent()
     }
 
+    private data class DumpCache(
+        val isCarrierMerged: Boolean,
+        val activeRepo: MobileConnectionRepositoryKairos,
+    )
+
     @AssistedFactory
     interface Factory {
         fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
index e468159..e6c2921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
@@ -131,6 +131,8 @@
     private val mobileRepoFactory: Lazy<ConnectionRepoFactory>,
 ) : MobileConnectionsRepositoryKairos, Dumpable, KairosBuilder by kairosBuilder() {
 
+    private var dumpCache: DumpCache? = null
+
     init {
         dumpManager.registerNormalDumpable("MobileConnectionsRepositoryKairos", this)
     }
@@ -253,6 +255,7 @@
                 .asIncremental()
                 .mapValues { (subId, sub) -> mobileRepoFactory.get().create(subId) }
                 .applyLatestSpecForKey()
+                .apply { observe { dumpCache = DumpCache(it) } }
         }
 
     private val telephonyManagerState: State<Pair<Int?, Set<Int>>> = buildState {
@@ -479,10 +482,6 @@
             profileClass = profileClass,
         )
 
-    private var dumpCache: DumpCache? = null
-
-    private data class DumpCache(val repos: Map<Int, FullMobileConnectionRepositoryKairos>)
-
     override fun dump(pw: PrintWriter, args: Array<String>) {
         val cache = dumpCache ?: return
         val ipw = IndentingPrintWriter(pw, " ")
@@ -494,10 +493,16 @@
 
         ipw.println("Connections (${cache.repos.size} total):")
         ipw.increaseIndent()
-        cache.repos.values.forEach { it.dump(ipw) }
+        cache.repos.values.forEach {
+            if (it is FullMobileConnectionRepositoryKairos) {
+                it.dump(ipw)
+            }
+        }
         ipw.decreaseIndent()
     }
 
+    private data class DumpCache(val repos: Map<Int, MobileConnectionRepositoryKairos>)
+
     fun interface ConnectionRepoFactory {
         fun create(subId: Int): BuildSpec<MobileConnectionRepositoryKairos>
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 0eabb4ec..af4e61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -61,31 +61,31 @@
      * consider this connection to be serving data, and thus want to show a network type icon, when
      * data is connected. Other data connection states would typically cause us not to show the icon
      */
-    val isDataConnected: StateFlow<Boolean>
+    val isDataConnected: Flow<Boolean>
 
     /** True if we consider this connection to be in service, i.e. can make calls */
-    val isInService: StateFlow<Boolean>
+    val isInService: Flow<Boolean>
 
     /** True if this connection is emergency only */
-    val isEmergencyOnly: StateFlow<Boolean>
+    val isEmergencyOnly: Flow<Boolean>
 
     /** Observable for the data enabled state of this connection */
-    val isDataEnabled: StateFlow<Boolean>
+    val isDataEnabled: Flow<Boolean>
 
     /** True if the RAT icon should always be displayed and false otherwise. */
-    val alwaysShowDataRatIcon: StateFlow<Boolean>
+    val alwaysShowDataRatIcon: Flow<Boolean>
 
     /** Canonical representation of the current mobile signal strength as a triangle. */
-    val signalLevelIcon: StateFlow<SignalIconModel>
+    val signalLevelIcon: Flow<SignalIconModel>
 
     /** Observable for RAT type (network type) indicator */
-    val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
+    val networkTypeIconGroup: Flow<NetworkTypeIconModel>
 
     /** Whether or not to show the slice attribution */
-    val showSliceAttribution: StateFlow<Boolean>
+    val showSliceAttribution: Flow<Boolean>
 
     /** True if this connection is satellite-based */
-    val isNonTerrestrial: StateFlow<Boolean>
+    val isNonTerrestrial: Flow<Boolean>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -95,7 +95,7 @@
      *    override in [connectionInfo.operatorAlphaShort], a value that is derived from
      *    [ServiceState]
      */
-    val networkName: StateFlow<NetworkNameModel>
+    val networkName: Flow<NetworkNameModel>
 
     /**
      * Provider name for this network connection. The name can be one of 3 values:
@@ -108,26 +108,26 @@
      * TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
      *   provided is identical
      */
-    val carrierName: StateFlow<String>
+    val carrierName: Flow<String>
 
     /** True if there is only one active subscription. */
-    val isSingleCarrier: StateFlow<Boolean>
+    val isSingleCarrier: Flow<Boolean>
 
     /**
      * True if this connection is considered roaming. The roaming bit can come from [ServiceState],
      * or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
      * connection to be roaming while carrier network change is active
      */
-    val isRoaming: StateFlow<Boolean>
+    val isRoaming: Flow<Boolean>
 
     /** See [MobileIconsInteractor.isForceHidden]. */
     val isForceHidden: Flow<Boolean>
 
     /** See [MobileConnectionRepository.isAllowedDuringAirplaneMode]. */
-    val isAllowedDuringAirplaneMode: StateFlow<Boolean>
+    val isAllowedDuringAirplaneMode: Flow<Boolean>
 
     /** True when in carrier network change mode */
-    val carrierNetworkChangeActive: StateFlow<Boolean>
+    val carrierNetworkChangeActive: Flow<Boolean>
 }
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
index 87877b3..6b9c537 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
@@ -32,11 +32,20 @@
 import com.android.systemui.kairos.mapValues
 import com.android.systemui.kairos.toColdConflatedFlow
 import com.android.systemui.kairosBuilder
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import dagger.Provides
 import dagger.multibindings.ElementsIntoSet
 import javax.inject.Inject
@@ -45,6 +54,8 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 @ExperimentalKairosApi
@@ -60,6 +71,7 @@
     context: Context,
     mobileMappingsProxy: MobileMappingsProxy,
     private val userSetupRepo: UserSetupRepository,
+    private val logFactory: TableLogBufferFactory,
 ) : MobileIconsInteractor, KairosBuilder by kairosBuilder() {
 
     private val interactorsBySubIdK = buildIncremental {
@@ -158,7 +170,37 @@
         get() = repo.isDeviceEmergencyCallCapable
 
     override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
-        interactorsBySubId.value[subId] ?: error("Unknown subscription id: $subId")
+        object : MobileIconInteractor {
+            override val tableLogBuffer: TableLogBuffer =
+                logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
+            override val activity: Flow<DataActivityModel> = latest { activity }
+            override val mobileIsDefault: Flow<Boolean> = latest { mobileIsDefault }
+            override val isDataConnected: Flow<Boolean> = latest { isDataConnected }
+            override val isInService: Flow<Boolean> = latest { isInService }
+            override val isEmergencyOnly: Flow<Boolean> = latest { isEmergencyOnly }
+            override val isDataEnabled: Flow<Boolean> = latest { isDataEnabled }
+            override val alwaysShowDataRatIcon: Flow<Boolean> = latest { alwaysShowDataRatIcon }
+            override val signalLevelIcon: Flow<SignalIconModel> = latest { signalLevelIcon }
+            override val networkTypeIconGroup: Flow<NetworkTypeIconModel> = latest {
+                networkTypeIconGroup
+            }
+            override val showSliceAttribution: Flow<Boolean> = latest { showSliceAttribution }
+            override val isNonTerrestrial: Flow<Boolean> = latest { isNonTerrestrial }
+            override val networkName: Flow<NetworkNameModel> = latest { networkName }
+            override val carrierName: Flow<String> = latest { carrierName }
+            override val isSingleCarrier: Flow<Boolean> = latest { isSingleCarrier }
+            override val isRoaming: Flow<Boolean> = latest { isRoaming }
+            override val isForceHidden: Flow<Boolean> = latest { isForceHidden }
+            override val isAllowedDuringAirplaneMode: Flow<Boolean> = latest {
+                isAllowedDuringAirplaneMode
+            }
+            override val carrierNetworkChangeActive: Flow<Boolean> = latest {
+                carrierNetworkChangeActive
+            }
+
+            private fun <T> latest(block: MobileIconInteractor.() -> Flow<T>): Flow<T> =
+                interactorsBySubId.flatMapLatestConflated { it[subId]?.block() ?: emptyFlow() }
+        }
 
     @dagger.Module
     object Module {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9a81992..7f778bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -256,6 +256,9 @@
                     if (mClockFormat != null) {
                         mClockFormat.setTimeZone(mCalendar.getTimeZone());
                     }
+                    if (mContentDescriptionFormat != null) {
+                        mContentDescriptionFormat.setTimeZone(mCalendar.getTimeZone());
+                    }
                 });
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 final Locale newLocale = getResources().getConfiguration().locale;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 15cb95a..846db63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,7 @@
  */
 package com.android.systemui;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -49,11 +50,13 @@
 
 import org.junit.After;
 import org.junit.AfterClass;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.mockito.Mockito;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -190,6 +193,7 @@
 
     @Before
     public void SysuiSetup() throws Exception {
+        assertTempFilesAreCreatable();
         ProtoLog.REQUIRE_PROTOLOGTOOL = false;
         mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
         mDependency = mSysuiDependency.install();
@@ -211,6 +215,28 @@
         }
     }
 
+    private static Boolean sCanCreateTempFiles = null;
+
+    private static void assertTempFilesAreCreatable() {
+        // TODO(b/391948934): hopefully remove this hack
+        if (sCanCreateTempFiles == null) {
+            try {
+                File tempFile = File.createTempFile("confirm_temp_file_createable", "txt");
+                sCanCreateTempFiles = true;
+                assertTrue(tempFile.delete());
+            } catch (IOException e) {
+                sCanCreateTempFiles = false;
+                throw new RuntimeException(e);
+            }
+        }
+        if (!sCanCreateTempFiles) {
+            Assert.fail(
+                    "Cannot create temp files, so mockito will probably fail (b/391948934).  Temp"
+                            + " folder should be: "
+                            + System.getProperty("java.io.tmpdir"));
+        }
+    }
+
     protected boolean shouldFailOnLeakedReceiver() {
         return false;
     }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index b956e44..90a9271 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -281,7 +281,6 @@
                             },
                     )
                 }
-                downstreamSet.clear()
             }
         }
         reset()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index c11eb12..81f3702 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -145,7 +145,14 @@
                 val conn = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
         }
 
@@ -156,7 +163,14 @@
                 val conn = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
 
             // add new
@@ -343,13 +357,8 @@
                 val (patchesConn, needsEval) =
                     getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
                         ?: run {
-                            // Turns out we can't connect to patches, so update our depth and
-                            // propagate
-                            if (muxNode.depthTracker.setIsIndirectRoot(false)) {
-                                // TODO: schedules might not be necessary now that we're not
-                                // parallel?
-                                muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
-                            }
+                            // Turns out we can't connect to patches, so update our depth
+                            muxNode.depthTracker.setIsIndirectRoot(false)
                             return
                         }
                 muxNode.patches = patchesConn
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index cb2c6e5..faef6a2 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -109,7 +109,14 @@
                 val conn: NodeConnection<V> = branchNode.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = branchNode.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
         }
 
@@ -123,7 +130,14 @@
                 val conn: NodeConnection<V> = oldBranch.upstream
                 severed.add(conn)
                 conn.removeDownstream(downstream = oldBranch.schedulable)
-                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                if (conn.depthTracker.snapshotIsDirect) {
+                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+                } else {
+                    depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+                    depthTracker.updateIndirectRoots(
+                        removals = conn.depthTracker.snapshotIndirectRoots
+                    )
+                }
             }
 
             // add new
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d9db178..6e6d00d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1230,7 +1230,7 @@
                 "registerTabletModeChangedListener()")) {
             throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
         }
-        Objects.requireNonNull(listener, "event must not be null");
+        Objects.requireNonNull(listener, "listener must not be null");
 
         synchronized (mTabletModeLock) {
             final int callingPid = Binder.getCallingPid();
@@ -1342,7 +1342,7 @@
 
     @Override
     public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) {
-        Objects.requireNonNull(inputChannelToken, "event must not be null");
+        Objects.requireNonNull(inputChannelToken, "inputChannelToken must not be null");
 
         mNative.requestPointerCapture(inputChannelToken, enabled);
     }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 6db62c8..ccb9e3e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -301,7 +301,8 @@
             throw new IllegalArgumentException(
                     "Unknown session ID in closeSession: id=" + sessionId);
         }
-        halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
+        mEndpointManager.halCloseEndpointSession(
+                sessionId, ContextHubServiceUtil.toHalReason(reason));
     }
 
     @Override
@@ -312,7 +313,7 @@
             // Iterate in reverse since cleanupSessionResources will remove the entry
             for (int i = mSessionMap.size() - 1; i >= 0; i--) {
                 int id = mSessionMap.keyAt(i);
-                halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
+                mEndpointManager.halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
                 cleanupSessionResources(id);
             }
         }
@@ -444,7 +445,8 @@
                     int id = mSessionMap.keyAt(i);
                     HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
                     if (!hasEndpointPermissions(target)) {
-                        halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
+                        mEndpointManager.halCloseEndpointSessionNoThrow(
+                                id, Reason.PERMISSION_DENIED);
                         onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
                         // Resource cleanup is done in onCloseEndpointSession
                     }
@@ -503,17 +505,7 @@
         mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
     }
 
-    /* package */ void onEndpointSessionOpenRequest(
-            int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
-        Optional<Byte> error =
-                onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
-        if (error.isPresent()) {
-            halCloseEndpointSessionNoThrow(sessionId, error.get());
-            onCloseEndpointSession(sessionId, error.get());
-            // Resource cleanup is done in onCloseEndpointSession
-        }
-    }
-
+    /** Handle close endpoint callback to the client side */
     /* package */ void onCloseEndpointSession(int sessionId, byte reason) {
         if (!cleanupSessionResources(sessionId)) {
             Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
@@ -585,7 +577,7 @@
         }
     }
 
-    private Optional<Byte> onEndpointSessionOpenRequestInternal(
+    /* package */ Optional<Byte> onEndpointSessionOpenRequest(
             int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
         if (!hasEndpointPermissions(initiator)) {
             Log.e(
@@ -594,15 +586,41 @@
                             + initiator
                             + " doesn't have permission for "
                             + mEndpointInfo);
-            return Optional.of(Reason.PERMISSION_DENIED);
+            byte reason = Reason.PERMISSION_DENIED;
+            onCloseEndpointSession(sessionId, reason);
+            return Optional.of(reason);
         }
 
+        // Check & handle error cases for duplicated session id.
+        final boolean existingSession;
+        final boolean existingSessionActive;
         synchronized (mOpenSessionLock) {
             if (hasSessionId(sessionId)) {
-                Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
-                return Optional.of(Reason.UNSPECIFIED);
+                existingSession = true;
+                existingSessionActive = mSessionMap.get(sessionId).isActive();
+                Log.w(
+                        TAG,
+                        "onEndpointSessionOpenRequest: "
+                                + "Existing session ID: "
+                                + sessionId
+                                + ", isActive: "
+                                + existingSessionActive);
+            } else {
+                existingSession = false;
+                existingSessionActive = false;
+                mSessionMap.put(sessionId, new Session(initiator, true));
             }
-            mSessionMap.put(sessionId, new Session(initiator, true));
+        }
+
+        if (existingSession) {
+            if (existingSessionActive) {
+                // Existing session is already active, call onSessionOpenComplete.
+                openSessionRequestComplete(sessionId);
+                return Optional.empty();
+            }
+            // Reject the session open request for now. Consider invalidating previous pending
+            // session open request based on timeout.
+            return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
         }
 
         boolean success =
@@ -610,7 +628,11 @@
                         (consumer) ->
                                 consumer.onSessionOpenRequest(
                                         sessionId, initiator, serviceDescriptor));
-        return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
+        byte reason = Reason.UNSPECIFIED;
+        if (!success) {
+            onCloseEndpointSession(sessionId, reason);
+        }
+        return success ? Optional.empty() : Optional.of(reason);
     }
 
     private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
@@ -657,29 +679,6 @@
     }
 
     /**
-     * Calls the HAL closeEndpointSession API.
-     *
-     * @param sessionId The session ID to close
-     * @param halReason The HAL reason
-     */
-    private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
-        try {
-            mHubInterface.closeEndpointSession(sessionId, halReason);
-        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
-            throw e;
-        }
-    }
-
-    /** Same as halCloseEndpointSession but does not throw the exception */
-    private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
-        try {
-            halCloseEndpointSession(sessionId, halReason);
-        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
-            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
-        }
-    }
-
-    /**
      * Cleans up resources related to a session with the provided ID.
      *
      * @param sessionId The session ID to clean up resources for
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 8ab581e..e156159 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -29,6 +29,7 @@
 import android.hardware.contexthub.IContextHubEndpointCallback;
 import android.hardware.contexthub.IEndpointCommunication;
 import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -42,6 +43,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
@@ -316,6 +318,11 @@
         }
     }
 
+    /** Returns if a sessionId can be allocated for the service hub. */
+    private boolean isSessionIdAllocatedForService(int sessionId) {
+        return sessionId > mMaxSessionId || sessionId < mMinSessionId;
+    }
+
     /**
      * Unregisters an endpoint given its ID.
      *
@@ -337,8 +344,7 @@
         }
     }
 
-    @Override
-    public void onEndpointSessionOpenRequest(
+    private Optional<Byte> onEndpointSessionOpenRequestInternal(
             int sessionId,
             HubEndpointInfo.HubEndpointIdentifier destination,
             HubEndpointInfo.HubEndpointIdentifier initiator,
@@ -348,7 +354,7 @@
                     TAG,
                     "onEndpointSessionOpenRequest: invalid destination hub ID: "
                             + destination.getHub());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
         ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
         if (broker == null) {
@@ -356,7 +362,7 @@
                     TAG,
                     "onEndpointSessionOpenRequest: unknown destination endpoint ID: "
                             + destination.getEndpoint());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
         HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
         if (initiatorInfo == null) {
@@ -364,9 +370,29 @@
                     TAG,
                     "onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
                             + initiator.getEndpoint());
-            return;
+            return Optional.of(Reason.ENDPOINT_INVALID);
         }
-        broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+        if (!isSessionIdAllocatedForService(sessionId)) {
+            Log.e(
+                    TAG,
+                    "onEndpointSessionOpenRequest: invalid session ID, rejected:"
+                            + " sessionId="
+                            + sessionId);
+            return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+        }
+        return broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+    }
+
+    @Override
+    public void onEndpointSessionOpenRequest(
+            int sessionId,
+            HubEndpointInfo.HubEndpointIdentifier destination,
+            HubEndpointInfo.HubEndpointIdentifier initiator,
+            String serviceDescriptor) {
+        Optional<Byte> errorOptional =
+                onEndpointSessionOpenRequestInternal(
+                        sessionId, destination, initiator, serviceDescriptor);
+        errorOptional.ifPresent((error) -> halCloseEndpointSessionNoThrow(sessionId, error));
     }
 
     @Override
@@ -418,6 +444,30 @@
         }
     }
 
+    /**
+     * Calls the HAL closeEndpointSession API.
+     *
+     * @param sessionId The session ID to close
+     * @param halReason The HAL reason
+     */
+    /* package */ void halCloseEndpointSession(int sessionId, byte halReason)
+            throws RemoteException {
+        try {
+            mHubInterface.closeEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            throw e;
+        }
+    }
+
+    /** Same as halCloseEndpointSession but does not throw the exception */
+    /* package */ void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+        try {
+            halCloseEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 7b4c563..7fd400e 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -518,7 +518,6 @@
         if (!Flags.gnssAssistanceInterfaceJni()) {
             return;
         }
-        Preconditions.checkState(!mRegistered);
         Preconditions.checkState(mGnssAssistanceCallbacks == null);
         mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8948bd1..78554bd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6173,10 +6173,15 @@
         }
 
         @Override
-        public Map<String, AutomaticZenRule> getAutomaticZenRules() {
+        public ParceledListSlice getAutomaticZenRules() {
             int callingUid = Binder.getCallingUid();
             enforcePolicyAccess(callingUid, "getAutomaticZenRules");
-            return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
+            List<AutomaticZenRule.AzrWithId> ruleList = new ArrayList<>();
+            for (Map.Entry<String, AutomaticZenRule> rule : mZenModeHelper.getAutomaticZenRules(
+                    getCallingZenUser(), callingUid).entrySet()) {
+                ruleList.add(new AutomaticZenRule.AzrWithId(rule.getKey(), rule.getValue()));
+            }
+            return new ParceledListSlice<>(ruleList);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2798e84..ab87459 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -218,6 +218,11 @@
      */
     protected void adjustAppearance(@NonNull WindowState dimmingContainer,
                                     float alpha, int blurRadius) {
+        if (!mHost.isVisibleRequested()) {
+            // If the host is already going away, there is no point in keeping dimming
+            return;
+        }
+
         if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
             final DimState d = obtainDimState(dimmingContainer);
             d.prepareLookChange(alpha, blurRadius);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 51ed6bb..f055feb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -276,6 +276,7 @@
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
 import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
+import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE;
 import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER;
@@ -16296,6 +16297,13 @@
         return null;
     }
 
+    /**
+     * When multiple admins enforce a policy, this method returns an admin according to this order:
+     * 1. Supervision
+     * 2. DPC
+     *
+     * Otherwise, it returns any other admin.
+     */
     private android.app.admin.EnforcingAdmin getEnforcingAdminInternal(int userId,
             String identifier) {
         Objects.requireNonNull(identifier);
@@ -16304,16 +16312,22 @@
         if (admins.isEmpty()) {
             return null;
         }
-
-        final EnforcingAdmin admin;
         if (admins.size() == 1) {
-            admin = admins.iterator().next();
-        } else {
-            Optional<EnforcingAdmin> dpc = admins.stream()
-                    .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
-            admin = dpc.orElseGet(() -> admins.stream().findFirst().get());
+            return admins.iterator().next().getParcelableAdmin();
         }
-        return admin == null ? null : admin.getParcelableAdmin();
+        Optional<EnforcingAdmin> supervision = admins.stream()
+                .filter(a -> a.hasAuthority(
+                        EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE)))
+                .findFirst();
+        if (supervision.isPresent()) {
+            return supervision.get().getParcelableAdmin();
+        }
+        Optional<EnforcingAdmin> dpc = admins.stream()
+                .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
+        if (dpc.isPresent()) {
+            return dpc.get().getParcelableAdmin();
+        }
+        return admins.iterator().next().getParcelableAdmin();
     }
 
     private <V> Set<EnforcingAdmin> getEnforcingAdminsForIdentifier(int userId, String identifier) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 543e32f..9ff6eb6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -34,6 +34,7 @@
 import android.app.admin.PolicyKey;
 import android.app.admin.PolicyValue;
 import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
@@ -282,7 +283,9 @@
 
     static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
             new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
-            new MostRecent<>(),
+            (Flags.usePolicyIntersectionForPermittedInputMethods()
+                ? new StringSetIntersection()
+                : new MostRecent<>()),
             POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
             PolicyEnforcerCallbacks::noOp,
             new PackageSetPolicySerializer());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
new file mode 100644
index 0000000..bc075b02
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.PackageSetPolicyValue;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.Set;
+
+final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+    @Override
+    PolicyValue<Set<String>> resolve(
+            @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
+        Objects.requireNonNull(adminPolicies);
+        Set<String> intersectionOfPolicies = null;
+        for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+            if (intersectionOfPolicies == null) {
+                intersectionOfPolicies = new HashSet<>(policy.getValue());
+            } else {
+                intersectionOfPolicies.retainAll(policy.getValue());
+            }
+        }
+        if (intersectionOfPolicies == null) {
+            return null;
+        }
+        // Note that the resulting set below may be empty, but that's fine:
+        // particular policy should decide what is the meaning of an empty set.
+        return new PackageSetPolicyValue(intersectionOfPolicies);
+    }
+
+    @Override
+    android.app.admin.StringSetIntersection getParcelableResolutionMechanism() {
+        return new android.app.admin.StringSetIntersection();
+    }
+
+    @Override
+    public String toString() {
+        return "StringSetIntersection {}";
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 4d2dcf6..43b1ec3 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -67,12 +68,15 @@
     private static final int SESSION_ID_RANGE = ContextHubEndpointManager.SERVICE_SESSION_RANGE;
     private static final int MIN_SESSION_ID = 0;
     private static final int MAX_SESSION_ID = MIN_SESSION_ID + SESSION_ID_RANGE - 1;
+    private static final int SESSION_ID_FOR_OPEN_REQUEST = MAX_SESSION_ID + 1;
+    private static final int INVALID_SESSION_ID_FOR_OPEN_REQUEST = MIN_SESSION_ID + 1;
 
     private static final String ENDPOINT_NAME = "Example test endpoint";
     private static final int ENDPOINT_ID = 1;
     private static final String ENDPOINT_PACKAGE_NAME = "com.android.server.location.contexthub";
 
     private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
+    private static final String ENDPOINT_SERVICE_DESCRIPTOR = "serviceDescriptor";
     private static final int TARGET_ENDPOINT_ID = 1;
 
     private static final int SAMPLE_MESSAGE_TYPE = 1234;
@@ -225,6 +229,105 @@
     }
 
     @Test
+    public void testEndpointSessionOpenRequest() throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+
+        verify(mMockCallback)
+                .onSessionOpenRequest(
+                        SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+
+        // Accept
+        endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+        verify(mMockEndpointCommunications)
+                .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
+    public void testEndpointSessionOpenRequestWithInvalidSessionId() throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+        verify(mMockEndpointCommunications)
+                .closeEndpointSession(
+                        INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                        Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+        verify(mMockCallback, never())
+                .onSessionOpenRequest(
+                        INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+                        targetInfo,
+                        ENDPOINT_SERVICE_DESCRIPTOR);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
+    public void testEndpointSessionOpenRequest_duplicatedSessionId_noopWhenSessionIsActive()
+            throws RemoteException {
+        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+        IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+        HubEndpointInfo targetInfo =
+                new HubEndpointInfo(
+                        TARGET_ENDPOINT_NAME,
+                        TARGET_ENDPOINT_ID,
+                        ENDPOINT_PACKAGE_NAME,
+                        Collections.emptyList());
+        mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+        endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+        // Now session with id SESSION_ID_FOR_OPEN_REQUEST is active
+
+        // Duplicated session open request
+        mEndpointManager.onEndpointSessionOpenRequest(
+                SESSION_ID_FOR_OPEN_REQUEST,
+                endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+                targetInfo.getIdentifier(),
+                ENDPOINT_SERVICE_DESCRIPTOR);
+
+        // Client API is only invoked once
+        verify(mMockCallback, times(1))
+                .onSessionOpenRequest(
+                        SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+        // HAL still receives two open complete notifications
+        verify(mMockEndpointCommunications, times(2))
+                .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+        unregisterExampleEndpoint(endpoint);
+    }
+
+    @Test
     public void testMessageTransaction() throws RemoteException {
         IContextHubEndpoint endpoint = registerExampleEndpoint();
         testMessageTransactionInternal(endpoint, /* deliverMessageStatus= */ true);
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 bc8b7be..902171d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16844,22 +16844,22 @@
     public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
         setUpRealZenTest();
         mService.setCallerIsNormalPackage();
-        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
 
         // Create an implicit zen rule by calling setNotificationPolicy from an app.
         mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
-        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
-        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(rule.getValue().getOwner()).isNull();
-        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+        AutomaticZenRule.AzrWithId rule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(rule.mRule.getOwner()).isNull();
+        assertThat(rule.mRule.getConfigurationActivity()).isNull();
 
         // Now try to update said rule (e.g. disable it). Should fail.
         // We also validate the exception message because NPE could be thrown by all sorts of test
         // issues (e.g. misconfigured mocks).
-        rule.getValue().setEnabled(false);
+        rule.mRule.setEnabled(false);
         NullPointerException e = assertThrows(NullPointerException.class,
-                () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
+                () -> mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false));
         assertThat(e.getMessage()).isEqualTo(
                 "Rule must have a ConditionProviderService and/or configuration activity");
     }
@@ -16869,24 +16869,24 @@
     public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
         setUpRealZenTest();
         mService.setCallerIsNormalPackage();
-        assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
 
         // Create an implicit zen rule by calling setNotificationPolicy from an app.
         mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
-        assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
-        Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(rule.getValue().getOwner()).isNull();
-        assertThat(rule.getValue().getConfigurationActivity()).isNull();
+        assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+        AutomaticZenRule.AzrWithId rule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(rule.mRule.getOwner()).isNull();
+        assertThat(rule.mRule.getConfigurationActivity()).isNull();
 
         // Now update said rule from Settings (e.g. disable it). Should work!
         mService.isSystemUid = true;
-        rule.getValue().setEnabled(false);
-        mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
+        rule.mRule.setEnabled(false);
+        mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false);
 
-        Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
-                mBinderService.getAutomaticZenRules().entrySet());
-        assertThat(updatedRule.getValue().isEnabled()).isFalse();
+        AutomaticZenRule.AzrWithId updatedRule = getOnlyElement(
+                (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+        assertThat(updatedRule.mRule.isEnabled()).isFalse();
     }
 
     @Test