Merge "Harden Factory Reset Protection" into main
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 324d8ca..7284f47 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -23,7 +23,6 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.util.TimeUtils.formatDuration;
 
 import android.annotation.BytesLong;
@@ -50,9 +49,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.Trace;
-import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -206,6 +203,8 @@
     /* Minimum flex for a periodic job, in milliseconds. */
     private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes
 
+    private static final long MIN_ALLOWED_TIME_WINDOW_MILLIS = MIN_PERIOD_MILLIS;
+
     /**
      * Minimum backoff interval for a job, in milliseconds
      * @hide
@@ -1881,11 +1880,12 @@
         }
 
         /**
-         * Set deadline which is the maximum scheduling latency. The job will be run by this
-         * deadline even if other requirements (including a delay set through
-         * {@link #setMinimumLatency(long)}) are not met.
+         * Set a deadline after which all other functional requested constraints will be ignored.
+         * After the deadline has passed, the job can run even if other requirements (including
+         * a delay set through {@link #setMinimumLatency(long)}) are not met.
          * {@link JobParameters#isOverrideDeadlineExpired()} will return {@code true} if the job's
-         * deadline has passed.
+         * deadline has passed. The job's execution may be delayed beyond the set deadline by
+         * other factors such as Doze mode and system health signals.
          *
          * <p>
          * Because it doesn't make sense setting this property on a periodic job, doing so will
@@ -1894,30 +1894,23 @@
          *
          * <p class="note">
          * Since a job will run once the deadline has passed regardless of the status of other
-         * constraints, setting a deadline of 0 with other constraints makes those constraints
-         * meaningless when it comes to execution decisions. Avoid doing this.
-         * </p>
-         *
-         * <p>
-         * Short deadlines hinder the system's ability to optimize scheduling behavior and may
-         * result in running jobs at inopportune times. Therefore, starting in Android version
-         * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, minimum time windows will be
-         * enforced to help make it easier to better optimize job execution. Time windows are
+         * constraints, setting a deadline of 0 (or a {@link #setMinimumLatency(long) delay} equal
+         * to the deadline) with other constraints makes those constraints
+         * meaningless when it comes to execution decisions. Since doing so is indicative of an
+         * error in the logic, starting in Android version
+         * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, jobs with extremely short
+         * time windows will fail to build. Time windows are
          * defined as the time between a job's {@link #setMinimumLatency(long) minimum latency}
          * and its deadline. If the minimum latency is not set, it is assumed to be 0.
-         * The following minimums will be enforced:
-         * <ul>
-         *     <li>
-         *         Jobs with {@link #PRIORITY_DEFAULT} or higher priorities have a minimum time
-         *         window of one hour.
-         *     </li>
-         *     <li>Jobs with {@link #PRIORITY_LOW} have a minimum time window of 6 hours.</li>
-         *     <li>Jobs with {@link #PRIORITY_MIN} have a minimum time window of 12 hours.</li>
-         * </ul>
          *
          * Work that must happen immediately should use {@link #setExpedited(boolean)} or
          * {@link #setUserInitiated(boolean)} in the appropriate manner.
          *
+         * <p>
+         * This API aimed to guarantee execution of the job by the deadline only on Android version
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP}. That aim and guarantee has not existed
+         * since {@link android.os.Build.VERSION_CODES#M}.
+         *
          * @see JobInfo#getMaxExecutionDelayMillis()
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
@@ -2347,35 +2340,36 @@
                 throw new IllegalArgumentException("Invalid priority level provided: " + mPriority);
         }
 
-        if (enforceMinimumTimeWindows
-                && Flags.enforceMinimumTimeWindows()
-                // TODO(312197030): remove exemption for the system
-                && !UserHandle.isCore(Process.myUid())
-                && hasLateConstraint && !isPeriodic) {
-            final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
-            if (mPriority >= PRIORITY_DEFAULT) {
-                if (maxExecutionDelayMillis - windowStart < HOUR_IN_MILLIS) {
-                    throw new IllegalArgumentException(
-                            getPriorityString(mPriority)
-                                    + " cannot have a time window less than 1 hour."
-                                    + " Delay=" + windowStart
-                                    + ", deadline=" + maxExecutionDelayMillis);
-                }
-            } else if (mPriority >= PRIORITY_LOW) {
-                if (maxExecutionDelayMillis - windowStart < 6 * HOUR_IN_MILLIS) {
-                    throw new IllegalArgumentException(
-                            getPriorityString(mPriority)
-                                    + " cannot have a time window less than 6 hours."
-                                    + " Delay=" + windowStart
-                                    + ", deadline=" + maxExecutionDelayMillis);
-                }
+        final boolean hasFunctionalConstraint = networkRequest != null
+                || constraintFlags != 0
+                || (triggerContentUris != null && triggerContentUris.length > 0);
+        if (hasLateConstraint && !isPeriodic) {
+            if (!hasFunctionalConstraint) {
+                Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
+                        + " has a deadline with no functional constraints."
+                        + " The deadline won't improve job execution latency."
+                        + " Consider removing the deadline.");
             } else {
-                if (maxExecutionDelayMillis - windowStart < 12 * HOUR_IN_MILLIS) {
-                    throw new IllegalArgumentException(
-                            getPriorityString(mPriority)
-                                    + " cannot have a time window less than 12 hours."
-                                    + " Delay=" + windowStart
-                                    + ", deadline=" + maxExecutionDelayMillis);
+                final long windowStart = hasEarlyConstraint ? minLatencyMillis : 0;
+                if (maxExecutionDelayMillis - windowStart < MIN_ALLOWED_TIME_WINDOW_MILLIS) {
+                    if (enforceMinimumTimeWindows
+                            && Flags.enforceMinimumTimeWindows()) {
+                        throw new IllegalArgumentException("Jobs with a deadline and"
+                                + " functional constraints cannot have a time window less than "
+                                + MIN_ALLOWED_TIME_WINDOW_MILLIS + " ms."
+                                + " Job '" + service.flattenToShortString() + "#" + jobId + "'"
+                                + " has delay=" + windowStart
+                                + ", deadline=" + maxExecutionDelayMillis);
+                    } else {
+                        Log.w(TAG, "Job '" + service.flattenToShortString() + "#" + jobId + "'"
+                                + " has a deadline with functional constraints and an extremely"
+                                + " short time window of "
+                                + (maxExecutionDelayMillis - windowStart) + " ms"
+                                + " (delay=" + windowStart
+                                + ", deadline=" + maxExecutionDelayMillis + ")."
+                                + " The functional constraints are not likely to be satisfied when"
+                                + " the job runs.");
+                    }
                 }
             }
         }
diff --git a/api/Android.bp b/api/Android.bp
index a148cbd..9d2147c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -361,7 +361,10 @@
     previous_api: ":android.api.public.latest",
     merge_annotations_dirs: ["metalava-manual"],
     defaults_visibility: ["//frameworks/base/api"],
-    visibility: ["//frameworks/base/api"],
+    visibility: [
+        "//frameworks/base/api",
+        "//frameworks/base/core/api",
+    ],
 }
 
 // We resolve dependencies on APIs in modules by depending on a prebuilt of the whole
diff --git a/api/api.go b/api/api.go
index fa2be21..c733f5b 100644
--- a/api/api.go
+++ b/api/api.go
@@ -130,7 +130,7 @@
 	Scope string
 }
 
-func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition, stubsTypeSuffix string, doDist bool) {
 	metalavaCmd := "$(location metalava)"
 	// Silence reflection warnings. See b/168689341
 	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
@@ -140,7 +140,7 @@
 	if txt.Scope != "public" {
 		filename = txt.Scope + "-" + filename
 	}
-	moduleName := ctx.ModuleName() + "-" + filename
+	moduleName := ctx.ModuleName() + stubsTypeSuffix + filename
 
 	props := genruleProps{}
 	props.Name = proptools.StringPtr(moduleName)
@@ -148,17 +148,19 @@
 	props.Out = []string{filename}
 	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
 	props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
-	props.Dists = []android.Dist{
-		{
-			Targets: []string{"droidcore"},
-			Dir:     proptools.StringPtr("api"),
-			Dest:    proptools.StringPtr(filename),
-		},
-		{
-			Targets: []string{"api_txt", "sdk"},
-			Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
-			Dest:    proptools.StringPtr(txt.DistFilename),
-		},
+	if doDist {
+		props.Dists = []android.Dist{
+			{
+				Targets: []string{"droidcore"},
+				Dir:     proptools.StringPtr("api"),
+				Dest:    proptools.StringPtr(filename),
+			},
+			{
+				Targets: []string{"api_txt", "sdk"},
+				Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+				Dest:    proptools.StringPtr(txt.DistFilename),
+			},
+		}
 	}
 	props.Visibility = []string{"//visibility:public"}
 	ctx.CreateModule(genrule.GenRuleFactory, &props)
@@ -343,7 +345,7 @@
 	ctx.CreateModule(android.FileGroupFactory, &props)
 }
 
-func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string, baseTxtModulePrefix, stubsTypeSuffix string, doDist bool) {
 	var textFiles []MergedTxtDefinition
 
 	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
@@ -352,7 +354,7 @@
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename:  f,
 			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-" + f,
+			BaseTxt:      ":" + baseTxtModulePrefix + f,
 			Modules:      bootclasspath,
 			ModuleTag:    "{.public" + tagSuffix[i],
 			Scope:        "public",
@@ -360,7 +362,7 @@
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename:  f,
 			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-system-" + f,
+			BaseTxt:      ":" + baseTxtModulePrefix + "system-" + f,
 			Modules:      bootclasspath,
 			ModuleTag:    "{.system" + tagSuffix[i],
 			Scope:        "system",
@@ -368,7 +370,7 @@
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename:  f,
 			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-module-lib-" + f,
+			BaseTxt:      ":" + baseTxtModulePrefix + "module-lib-" + f,
 			Modules:      bootclasspath,
 			ModuleTag:    "{.module-lib" + tagSuffix[i],
 			Scope:        "module-lib",
@@ -376,14 +378,14 @@
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename:  f,
 			DistFilename: distFilename[i],
-			BaseTxt:      ":non-updatable-system-server-" + f,
+			BaseTxt:      ":" + baseTxtModulePrefix + "system-server-" + f,
 			Modules:      system_server_classpath,
 			ModuleTag:    "{.system-server" + tagSuffix[i],
 			Scope:        "system-server",
 		})
 	}
 	for _, txt := range textFiles {
-		createMergedTxt(ctx, txt)
+		createMergedTxt(ctx, txt, stubsTypeSuffix, doDist)
 	}
 }
 
@@ -465,7 +467,8 @@
 		bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
 		sort.Strings(bootclasspath)
 	}
-	createMergedTxts(ctx, bootclasspath, system_server_classpath)
+	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
+	createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
 
 	createMergedPublicStubs(ctx, bootclasspath)
 	createMergedSystemStubs(ctx, bootclasspath)
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 8d8a82b..77594b7 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -96,3 +96,54 @@
     name: "non-updatable-test-lint-baseline.txt",
     srcs: ["test-lint-baseline.txt"],
 }
+
+// Exportable stub artifacts
+filegroup {
+    name: "non-updatable-exportable-current.txt",
+    srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-removed.txt",
+    srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-system-current.txt",
+    srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-system-removed.txt",
+    srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-module-lib-current.txt",
+    srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-module-lib-removed.txt",
+    srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-test-current.txt",
+    srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-test-removed.txt",
+    srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-system-server-current.txt",
+    srcs: [":services-non-updatable-stubs{.exportable.api.txt}"],
+}
+
+filegroup {
+    name: "non-updatable-exportable-system-server-removed.txt",
+    srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"],
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index b5a65d5..f41982f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1375,6 +1375,7 @@
     field public static final int reqTouchScreen = 16843303; // 0x1010227
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
     field public static final int requestRawExternalStorageAccess = 16844357; // 0x1010645
+    field @FlaggedApi("android.security.content_uri_permission_apis") public static final int requireContentUriPermissionFromCaller;
     field public static final int requireDeviceScreenOn = 16844317; // 0x101061d
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -8070,8 +8071,8 @@
     method public boolean isUsbDataSignalingEnabled();
     method public boolean isUsingUnifiedPassword(@NonNull android.content.ComponentName);
     method @NonNull public java.util.List<android.os.UserHandle> listForegroundAffiliatedUsers();
-    method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow();
-    method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK, conditional=true) public void lockNow(int);
+    method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow();
+    method @RequiresPermission(value="android.permission.LOCK_DEVICE", conditional=true) public void lockNow(int);
     method public int logoutUser(@NonNull android.content.ComponentName);
     method public void reboot(@NonNull android.content.ComponentName);
     method public void removeActiveAdmin(@NonNull android.content.ComponentName);
@@ -46312,6 +46313,7 @@
     method @NonNull public android.telephony.euicc.EuiccManager createForCardId(int);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void deleteSubscription(int, android.app.PendingIntent);
     method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent);
+    method @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "carrier privileges"}) public long getAvailableMemoryInBytes();
     method @Nullable public String getEid();
     method @Nullable public android.telephony.euicc.EuiccInfo getEuiccInfo();
     method public boolean isEnabled();
@@ -46344,6 +46346,7 @@
     field public static final int ERROR_SIM_MISSING = 10008; // 0x2718
     field public static final int ERROR_TIME_OUT = 10005; // 0x2715
     field public static final int ERROR_UNSUPPORTED_VERSION = 10007; // 0x2717
+    field @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") public static final long EUICC_MEMORY_FIELD_UNAVAILABLE = -1L; // 0xffffffffffffffffL
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2226622..45d5778 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11,6 +11,7 @@
     field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
     field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
     field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
+    field @FlaggedApi("android.multiuser.flags.enable_permission_to_access_hidden_profiles") public static final String ACCESS_HIDDEN_PROFILES_FULL = "android.permission.ACCESS_HIDDEN_PROFILES_FULL";
     field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
     field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String ACCESS_LAST_KNOWN_CELL_ID = "android.permission.ACCESS_LAST_KNOWN_CELL_ID";
     field public static final String ACCESS_LOCUS_ID_USAGE_STATS = "android.permission.ACCESS_LOCUS_ID_USAGE_STATS";
@@ -3151,16 +3152,38 @@
 
 package android.app.wearable {
 
+  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public final class WearableSensingDataRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDataSize();
+    method public int getDataType();
+    method public static int getMaxRequestSize();
+    method public static int getRateLimit();
+    method @NonNull public static java.time.Duration getRateLimitWindowSize();
+    method @NonNull public android.os.PersistableBundle getRequestDetails();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.wearable.WearableSensingDataRequest> CREATOR;
+  }
+
+  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final class WearableSensingDataRequest.Builder {
+    ctor public WearableSensingDataRequest.Builder(int);
+    method @NonNull public android.app.wearable.WearableSensingDataRequest build();
+    method @NonNull public android.app.wearable.WearableSensingDataRequest.Builder setRequestDetails(@NonNull android.os.PersistableBundle);
+  }
+
   public class WearableSensingManager {
+    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
     field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
     field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
     field public static final int STATUS_SUCCESS = 1; // 0x1
     field public static final int STATUS_UNKNOWN = 0; // 0x0
     field public static final int STATUS_UNSUPPORTED = 2; // 0x2
+    field @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8; // 0x8
     field @FlaggedApi("android.app.wearable.enable_unsupported_operation_status_code") public static final int STATUS_UNSUPPORTED_OPERATION = 6; // 0x6
     field public static final int STATUS_WEARABLE_UNAVAILABLE = 4; // 0x4
   }
@@ -4414,8 +4437,9 @@
   }
 
   @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable {
+    ctor public CancelSelectionRequest(@NonNull android.credentials.selection.RequestToken, boolean, @NonNull String);
     method public int describeContents();
-    method @NonNull public String getAppPackageName();
+    method @NonNull public String getPackageName();
     method @NonNull public android.credentials.selection.RequestToken getRequestToken();
     method public boolean shouldShowCancellationExplanation();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -4497,10 +4521,10 @@
 
   @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class RequestInfo implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public String getAppPackageName();
     method @Nullable public android.credentials.CreateCredentialRequest getCreateCredentialRequest();
     method @NonNull public java.util.List<java.lang.String> getDefaultProviderIds();
     method @Nullable public android.credentials.GetCredentialRequest getGetCredentialRequest();
+    method @NonNull public String getPackageName();
     method @NonNull public java.util.List<java.lang.String> getRegistryProviderIds();
     method @NonNull public android.credentials.selection.RequestToken getRequestToken();
     method @NonNull public String getType();
@@ -6953,6 +6977,8 @@
   @FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
+    method public static long getDefaultFadeInDurationMillis();
+    method public static long getDefaultFadeOutDurationMillis();
     method public long getFadeInDelayForOffenders();
     method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
     method public long getFadeInDurationForUsage(int);
@@ -6978,7 +7004,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.media.FadeManagerConfiguration> CREATOR;
     field public static final long DURATION_NOT_SET = 0L; // 0x0L
     field public static final int FADE_STATE_DISABLED = 0; // 0x0
-    field public static final int FADE_STATE_ENABLED_AUTO = 2; // 0x2
     field public static final int FADE_STATE_ENABLED_DEFAULT = 1; // 0x1
     field public static final String TAG = "FadeManagerConfiguration";
     field public static final int VOLUME_SHAPER_SYSTEM_FADE_ID = 2; // 0x2
@@ -6993,10 +7018,10 @@
     method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableContentType(int);
     method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableUid(int);
     method @NonNull public android.media.FadeManagerConfiguration build();
-    method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsage(int);
-    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
-    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentType(int);
-    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUid(int);
+    method @NonNull public android.media.FadeManagerConfiguration.Builder clearFadeableUsages();
+    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes();
+    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentTypes();
+    method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUids();
     method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
     method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
     method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
@@ -12403,6 +12428,7 @@
     method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
     method @Deprecated public abstract int onEraseSubscriptions(int);
     method public int onEraseSubscriptions(int, int);
+    method @FlaggedApi("com.android.internal.telephony.flags.esim_available_memory") public long onGetAvailableMemoryInBytes(int);
     method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
     method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
     method @NonNull public android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean);
@@ -13428,10 +13454,21 @@
 
 package android.service.wearable {
 
+  @FlaggedApi("android.app.wearable.enable_data_request_observer_api") public interface WearableSensingDataRequester {
+    method public void requestData(@NonNull android.app.wearable.WearableSensingDataRequest, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    field public static final int STATUS_OBSERVER_CANCELLED = 2; // 0x2
+    field public static final int STATUS_SUCCESS = 1; // 0x1
+    field public static final int STATUS_TOO_FREQUENT = 4; // 0x4
+    field public static final int STATUS_TOO_LARGE = 3; // 0x3
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+  }
+
   public abstract class WearableSensingService extends android.app.Service {
     ctor public WearableSensingService();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverRegistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
     method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -13610,6 +13647,13 @@
     method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
   }
 
+  public final class DisconnectCause implements android.os.Parcelable {
+    ctor @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public DisconnectCause(int, @NonNull CharSequence, @NonNull CharSequence, @NonNull String, int, int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable public android.telephony.ims.ImsReasonInfo getImsReasonInfo();
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyDisconnectCause();
+    method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyPreciseDisconnectCause();
+  }
+
   public abstract class InCallService extends android.app.Service {
     method @Deprecated public android.telecom.Phone getPhone();
     method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -14182,12 +14226,12 @@
     method @NonNull public android.telephony.DataThrottlingRequest.Builder setDataThrottlingAction(int);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public class DomainSelectionService extends android.app.Service {
+  @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public abstract class DomainSelectionService extends android.app.Service {
     ctor public DomainSelectionService();
     method public void onBarringInfoUpdated(int, int, @NonNull android.telephony.BarringInfo);
-    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method @NonNull public java.util.concurrent.Executor onCreateExecutor();
-    method public void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback);
+    method public abstract void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback);
     method public void onServiceStateUpdated(int, int, @NonNull android.telephony.ServiceState);
     field public static final int SCAN_TYPE_FULL_SERVICE = 2; // 0x2
     field public static final int SCAN_TYPE_LIMITED_SERVICE = 1; // 0x1
@@ -14201,7 +14245,7 @@
     method @Nullable public android.net.Uri getAddress();
     method @Nullable public String getCallId();
     method public int getCsDisconnectCause();
-    method @Nullable public android.telephony.EmergencyRegResult getEmergencyRegResult();
+    method @Nullable public android.telephony.EmergencyRegistrationResult getEmergencyRegistrationResult();
     method @Nullable public android.telephony.ims.ImsReasonInfo getPsDisconnectCause();
     method public int getSelectorType();
     method public int getSlotIndex();
@@ -14217,13 +14261,13 @@
   @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final class DomainSelectionService.SelectionAttributes.Builder {
     ctor public DomainSelectionService.SelectionAttributes.Builder(int, int, int);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes build();
-    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@NonNull android.net.Uri);
-    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@NonNull String);
+    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setAddress(@Nullable android.net.Uri);
+    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCallId(@Nullable String);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setCsDisconnectCause(int);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergency(boolean);
-    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegResult(@NonNull android.telephony.EmergencyRegResult);
+    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setEmergencyRegistrationResult(@Nullable android.telephony.EmergencyRegistrationResult);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setExitedFromAirplaneMode(boolean);
-    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@NonNull android.telephony.ims.ImsReasonInfo);
+    method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setPsDisconnectCause(@Nullable android.telephony.ims.ImsReasonInfo);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setTestEmergencyNumber(boolean);
     method @NonNull public android.telephony.DomainSelectionService.SelectionAttributes.Builder setVideoCall(boolean);
   }
@@ -14233,7 +14277,7 @@
     method public void reselectDomain(@NonNull android.telephony.DomainSelectionService.SelectionAttributes);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegResult implements android.os.Parcelable {
+  @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public final class EmergencyRegistrationResult implements android.os.Parcelable {
     method public int describeContents();
     method public int getAccessNetwork();
     method @NonNull public String getCountryIso();
@@ -14246,7 +14290,7 @@
     method public boolean isEmcBearerSupported();
     method public boolean isVopsSupported();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegResult> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.EmergencyRegistrationResult> CREATOR;
   }
 
   public final class ImsiEncryptionInfo implements android.os.Parcelable {
@@ -15318,7 +15362,7 @@
 
   @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public interface WwanSelectorCallback {
     method public void onDomainSelected(int, boolean);
-    method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegResult>);
+    method public void onRequestEmergencyNetworkScan(@NonNull java.util.List<java.lang.Integer>, int, boolean, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.telephony.EmergencyRegistrationResult>);
   }
 
 }
@@ -17386,6 +17430,19 @@
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public class EnableRequestAttributes {
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isDemoMode();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEmergencyMode();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEnabled();
+  }
+
+  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final class EnableRequestAttributes.Builder {
+    ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public EnableRequestAttributes.Builder(boolean);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes build();
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes.Builder setDemoMode(boolean);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.EnableRequestAttributes.Builder setEmergencyMode(boolean);
+  }
+
   @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class NtnSignalStrength implements android.os.Parcelable {
     ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public NtnSignalStrength(@Nullable android.telephony.satellite.NtnSignalStrength);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
@@ -17451,10 +17508,11 @@
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
-    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestEnabled(@NonNull android.telephony.satellite.EnableRequestAttributes, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+    method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsEmergencyModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -17516,6 +17574,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4
+    field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; // 0x18
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_ERROR = 5; // 0x5
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17; // 0x11
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19; // 0x13
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bbd6bde..fc095d4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1283,10 +1283,6 @@
     field public static final int RESULT_CODE_DIALOG_USER_CANCELED = 0; // 0x0
   }
 
-  @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CancelSelectionRequest implements android.os.Parcelable {
-    ctor @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public CancelSelectionRequest(@NonNull android.os.IBinder, boolean, @NonNull String);
-  }
-
   @FlaggedApi("android.credentials.flags.configurable_selector_ui_enabled") public final class CreateCredentialProviderData extends android.credentials.selection.ProviderData implements android.os.Parcelable {
     ctor public CreateCredentialProviderData(@NonNull String, @NonNull java.util.List<android.credentials.selection.Entry>, @Nullable android.credentials.selection.Entry);
     method @Nullable public android.credentials.selection.Entry getRemoteEntry();
@@ -1883,6 +1879,7 @@
     method @FlaggedApi("android.media.audio.focus_freeze_test_api") @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFocusUnmuteDelayAfterFadeOutForTest();
     method @Nullable public static android.media.AudioHalVersionInfo getHalVersion();
     method public static final int[] getPublicStreamTypes();
+    method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes();
     method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public float getRs2Value();
     method public int getStreamMinVolumeInt(int);
@@ -2056,6 +2053,10 @@
 
 package android.media.audiopolicy {
 
+  public class AudioPolicy {
+    method @FlaggedApi("android.media.audiopolicy.audio_mix_test_api") @NonNull public java.util.List<android.media.audiopolicy.AudioMix> getMixes();
+  }
+
   public static class AudioPolicy.Builder {
     method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
   }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 084c71f..a8d183a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5954,6 +5954,20 @@
     }
 
     /**
+     * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
+     * palette readiness.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY)
+    public void setThemeOverlayReady(boolean readiness) {
+        try {
+            getService().setThemeOverlayReady(readiness);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets the state of the {@link com.android.server.am.AppErrors} instance.
      * This is intended for use with CTS only.
      * @hide
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 232fc92..0ae2e01 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1258,4 +1258,11 @@
      */
     public abstract boolean clearApplicationUserData(String packageName, boolean keepState,
             boolean isRestore, IPackageDataObserver observer, int userId);
+
+    /**
+     * Returns current state of {@link com.android.systemui.theme.ThemeOverlayController} color
+     * palette readiness.
+     * @hide
+     */
+    public abstract boolean getThemeOverlayReadiness();
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b063d04..ceeaf5d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.PendingIntentInfo;
 import android.app.ActivityTaskManager;
 import android.app.ApplicationStartInfo;
 import android.app.ApplicationErrorReport;
@@ -553,6 +552,14 @@
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean isTopOfTask(in IBinder token);
     void bootAnimationComplete();
+
+    /**
+     * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
+     * palette readiness.
+     * @throws RemoteException
+     */
+    void setThemeOverlayReady(boolean readiness);
+
     @UnsupportedAppUsage
     void registerTaskStackListener(in ITaskStackListener listener);
     void unregisterTaskStackListener(in ITaskStackListener listener);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1ef4346..c8762c6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.LOCK_DEVICE;
 import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
@@ -6415,10 +6416,10 @@
      * (PIN, pattern, or password). This API is intended for use only by device admins.
      * <p>
      * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
-     * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
-     * true, then the method will return without completing any action. Before version
-     * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
-     * regardless of the caller's permissions.
+     * the LOCK_DEVICE permission or the device must have the
+     * device admin feature; if neither is true, then the method will return without completing
+     * any action. Before version {@link android.os.Build.VERSION_CODES#R},
+     * the device needed the device admin feature, regardless of the caller's permissions.
      * <p>
      * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
      * to be able to call this method; if it has not, a security exception will be thrown.
@@ -6438,7 +6439,8 @@
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
      */
-    @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true)
+    @SuppressLint("RequiresPermission")
+    @RequiresPermission(value = LOCK_DEVICE, conditional = true)
     public void lockNow() {
         lockNow(0);
     }
@@ -6449,14 +6451,13 @@
      * <p>
      * This method secures the device in response to an urgent situation, such as a lost or stolen
      * device. After this method is called, the device must be unlocked using strong authentication
-     * (PIN, pattern, or password). This API is for use only by device admins and holders of the
-     * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission.
+     * (PIN, pattern, or password). This API is intended for use only by device admins.
      * <p>
      * From version {@link android.os.Build.VERSION_CODES#R} onwards, the caller must either have
-     * the LOCK_DEVICE permission or the device must have the device admin feature; if neither is
-     * true, then the method will return without completing any action. Before version
-     * {@link android.os.Build.VERSION_CODES#R}, the device needed the device admin feature,
-     * regardless of the caller's permissions.
+     * the LOCK_DEVICE permission or the device must have the
+     * device admin feature; if neither is true, then the method will return without completing any
+     * action. Before version {@link android.os.Build.VERSION_CODES#R}, the device needed the device
+     * admin feature, regardless of the caller's permissions.
      * <p>
      * A calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK}
      * to be able to call this method; if it has not, a security exception will be thrown.
@@ -6485,7 +6486,7 @@
      * @param flags May be 0 or {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}.
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} and the does not hold
-     *             the {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK} permission, or
+     *             the {@link android.Manifest.permission#LOCK_DEVICE} permission, or
      *             the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is passed by an
      *             application that is not a profile owner of a managed profile.
      * @throws IllegalArgumentException if the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag is
@@ -6494,7 +6495,7 @@
      *             flag is passed when {@link #getStorageEncryptionStatus} does not return
      *             {@link #ENCRYPTION_STATUS_ACTIVE_PER_USER}.
      */
-    @RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK, conditional = true)
+    @RequiresPermission(value = LOCK_DEVICE, conditional = true)
     public void lockNow(@LockNowFlag int flags) {
         if (mService != null) {
             try {
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
index 9d55ce28..3cbc8a2 100644
--- a/core/java/android/app/wearable/IWearableSensingManager.aidl
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -16,6 +16,7 @@
 
 package android.app.wearable;
 
+import android.app.PendingIntent;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
@@ -33,4 +34,8 @@
      void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
      @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
      void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void registerDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback);
+     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+     void unregisterDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback);
 }
\ No newline at end of file
diff --git a/core/java/android/app/wearable/WearableSensingDataRequest.java b/core/java/android/app/wearable/WearableSensingDataRequest.java
new file mode 100644
index 0000000..9329b37
--- /dev/null
+++ b/core/java/android/app/wearable/WearableSensingDataRequest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.wearable;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.time.Duration;
+
+/**
+ * Data class for a data request for wearable sensing.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+@SystemApi
+public final class WearableSensingDataRequest implements Parcelable {
+    private static final int MAX_REQUEST_SIZE = 200;
+    private static final Duration RATE_LIMIT_WINDOW_SIZE = Duration.ofMinutes(1);
+    private static final int RATE_LIMIT = 30;
+
+    private final int mDataType;
+    @NonNull private final PersistableBundle mRequestDetails;
+
+    private WearableSensingDataRequest(int dataType, @NonNull PersistableBundle requestDetails) {
+        mDataType = dataType;
+        mRequestDetails = requestDetails;
+    }
+
+    /** Returns the data type this request is for. */
+    public int getDataType() {
+        return mDataType;
+    }
+
+    /** Returns the details for this request. */
+    @NonNull
+    public PersistableBundle getRequestDetails() {
+        return mRequestDetails;
+    }
+
+    /** Returns the data size of this object when it is parcelled. */
+    public int getDataSize() {
+        Parcel parcel = Parcel.obtain();
+        try {
+            writeToParcel(parcel, describeContents());
+            return parcel.dataSize();
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mDataType);
+        dest.writeTypedObject(mRequestDetails, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "WearableSensingDataRequest { "
+                + "dataType = "
+                + mDataType
+                + ", "
+                + "requestDetails = "
+                + mRequestDetails
+                + " }";
+    }
+
+    /**
+     * Returns a String representation of this data request that shows its contents.
+     *
+     * @hide
+     */
+    public String toExpandedString() {
+        if (mRequestDetails != null) {
+            // Trigger unparcelling so that its individual fields will be listed in toString
+            boolean unused =
+                    mRequestDetails.getBoolean(
+                            "PlaceholderForWearableSensingDataRequest#toExpandedString()");
+        }
+        return toString();
+    }
+
+    /**
+     * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+     *
+     * @hide
+     */
+    public static final String REQUEST_BUNDLE_KEY =
+            "android.app.wearable.WearableSensingDataRequestBundleKey";
+
+    /**
+     * The bundle key for the status callback for a data request, used in {@code
+     * RemoteCallback#sendResult}.
+     *
+     * @hide
+     */
+    public static final String REQUEST_STATUS_CALLBACK_BUNDLE_KEY =
+            "android.app.wearable.WearableSensingDataRequestStatusCallbackBundleKey";
+
+    public static final @NonNull Parcelable.Creator<WearableSensingDataRequest> CREATOR =
+            new Parcelable.Creator<WearableSensingDataRequest>() {
+                @Override
+                public WearableSensingDataRequest[] newArray(int size) {
+                    return new WearableSensingDataRequest[size];
+                }
+
+                @Override
+                public WearableSensingDataRequest createFromParcel(@NonNull Parcel in) {
+                    int dataType = in.readInt();
+                    PersistableBundle requestDetails =
+                            in.readTypedObject(PersistableBundle.CREATOR);
+                    return new WearableSensingDataRequest(dataType, requestDetails);
+                }
+            };
+
+    /**
+     * Returns the maximum allowed size of a WearableSensingDataRequest when it is parcelled.
+     * Instances that exceed this size can be constructed, but will be rejected by the system when
+     * they leave the isolated WearableSensingService.
+     */
+    public static int getMaxRequestSize() {
+        return MAX_REQUEST_SIZE;
+    }
+
+    /**
+     * Returns the rolling time window used to perform rate limiting on data requests leaving the
+     * WearableSensingService.
+     */
+    @NonNull
+    public static Duration getRateLimitWindowSize() {
+        return RATE_LIMIT_WINDOW_SIZE;
+    }
+
+    /**
+     * Returns the number of data requests allowed to leave the WearableSensingService in each
+     * {@link #getRateLimitWindowSize()}.
+     */
+    public static int getRateLimit() {
+        return RATE_LIMIT;
+    }
+
+    /** A builder for WearableSensingDataRequest. */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    public static final class Builder {
+        private int mDataType;
+        private PersistableBundle mRequestDetails;
+
+        public Builder(int dataType) {
+            mDataType = dataType;
+        }
+
+        /** Sets the request details. */
+        public @NonNull Builder setRequestDetails(@NonNull PersistableBundle requestDetails) {
+            mRequestDetails = requestDetails;
+            return this;
+        }
+
+        /** Builds the WearableSensingDataRequest. */
+        public @NonNull WearableSensingDataRequest build() {
+            if (mRequestDetails == null) {
+                mRequestDetails = PersistableBundle.EMPTY;
+            }
+            return new WearableSensingDataRequest(mDataType, mRequestDetails);
+        }
+    }
+}
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index 401d0b7..077f7b5 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -25,9 +25,11 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
 import android.app.ambientcontext.AmbientContextEvent;
 import android.companion.CompanionDeviceManager;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
@@ -58,7 +60,6 @@
  *
  * @hide
  */
-
 @SystemApi
 @SystemService(Context.WEARABLE_SENSING_SERVICE)
 public class WearableSensingManager {
@@ -71,6 +72,14 @@
     public static final String STATUS_RESPONSE_BUNDLE_KEY =
             "android.app.wearable.WearableSensingStatusBundleKey";
 
+    /**
+     * The Intent extra key for the data request in the Intent sent to the PendingIntent registered
+     * with {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_WEARABLE_SENSING_DATA_REQUEST =
+            "android.app.wearable.extra.WEARABLE_SENSING_DATA_REQUEST";
 
     /**
      * An unknown status.
@@ -107,6 +116,7 @@
      * The value of the status code that indicates the method called is not supported by the
      * implementation of {@link WearableSensingService}.
      */
+
     @FlaggedApi(Flags.FLAG_ENABLE_UNSUPPORTED_OPERATION_STATUS_CODE)
     public static final int STATUS_UNSUPPORTED_OPERATION = 6;
 
@@ -118,20 +128,42 @@
     @FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
     public static final int STATUS_CHANNEL_ERROR = 7;
 
+    /** The value of the status code that indicates the provided data type is not supported. */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    public static final int STATUS_UNSUPPORTED_DATA_TYPE = 8;
+
     /** @hide */
-    @IntDef(prefix = { "STATUS_" }, value = {
-            STATUS_UNKNOWN,
-            STATUS_SUCCESS,
-            STATUS_UNSUPPORTED,
-            STATUS_SERVICE_UNAVAILABLE,
-            STATUS_WEARABLE_UNAVAILABLE,
-            STATUS_ACCESS_DENIED,
-            STATUS_UNSUPPORTED_OPERATION,
-            STATUS_CHANNEL_ERROR
-    })
+    @IntDef(
+            prefix = {"STATUS_"},
+            value = {
+                STATUS_UNKNOWN,
+                STATUS_SUCCESS,
+                STATUS_UNSUPPORTED,
+                STATUS_SERVICE_UNAVAILABLE,
+                STATUS_WEARABLE_UNAVAILABLE,
+                STATUS_ACCESS_DENIED,
+                STATUS_UNSUPPORTED_OPERATION,
+                STATUS_CHANNEL_ERROR,
+                STATUS_UNSUPPORTED_DATA_TYPE
+            })
     @Retention(RetentionPolicy.SOURCE)
     public @interface StatusCode {}
 
+    /**
+     * Retrieves a {@link WearableSensingDataRequest} from the Intent sent to the PendingIntent
+     * provided to {@link #registerDataRequestObserver(int, PendingIntent, Executor, Consumer)}.
+     *
+     * @param intent The Intent received from the PendingIntent.
+     * @return The WearableSensingDataRequest in the provided Intent, or null if the Intent does not
+     *     contain a WearableSensingDataRequest.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    @Nullable
+    public static WearableSensingDataRequest getDataRequestFromIntent(@NonNull Intent intent) {
+        return intent.getParcelableExtra(
+                EXTRA_WEARABLE_SENSING_DATA_REQUEST, WearableSensingDataRequest.class);
+    }
+
     private final Context mContext;
     private final IWearableSensingManager mService;
 
@@ -256,6 +288,99 @@
         }
     }
 
+    /**
+     * Registers a data request observer for the provided data type.
+     *
+     * <p>When data is requested, the provided {@code dataRequestPendingIntent} will be invoked. A
+     * {@link WearableSensingDataRequest} can be extracted from the Intent sent to {@code
+     * dataRequestPendingIntent} by calling {@link #getDataRequestFromIntent(Intent)}. The observer
+     * can then provide the requested data via {@link #provideData(PersistableBundle, SharedMemory,
+     * Executor, Consumer)}.
+     *
+     * <p>There is no limit to the number of observers registered for a data type. How they are
+     * handled depends on the implementation of WearableSensingService.
+     *
+     * <p>When the observer is no longer needed, {@link #unregisterDataRequestObserver(int,
+     * PendingIntent, Executor, Consumer)} should be called with the same {@code
+     * dataRequestPendingIntent}. It should be done regardless of the status code returned from
+     * {@code statusConsumer} in order to clean up housekeeping data for the {@code
+     * dataRequestPendingIntent} maintained by the system.
+     *
+     * <p>Example:
+     *
+     * <pre>{@code
+     * // Create a PendingIntent for MyDataRequestBroadcastReceiver
+     * Intent intent =
+     *         new Intent(actionString).setClass(context, MyDataRequestBroadcastReceiver.class);
+     * PendingIntent pendingIntent = PendingIntent.getBroadcast(
+     *         context, 0, intent, PendingIntent.FLAG_MUTABLE);
+     *
+     * // Register the PendingIntent as a data request observer
+     * wearableSensingManager.registerDataRequestObserver(
+     *         dataType, pendingIntent, executor, statusConsumer);
+     *
+     * // Within MyDataRequestBroadcastReceiver, receive the broadcast Intent and extract the
+     * // WearableSensingDataRequest
+     * {@literal @}Override
+     * public void onReceive(Context context, Intent intent) {
+     *     WearableSensingDataRequest dataRequest =
+     *             WearableSensingManager.getDataRequestFromIntent(intent);
+     *     // After parsing the dataRequest, provide the data
+     *     wearableSensingManager.provideData(data, sharedMemory, executor, statusConsumer);
+     * }
+     * }</pre>
+     *
+     * @param dataType The data type to listen to. Values are defined by the application that
+     *     implements {@link WearableSensingService}.
+     * @param dataRequestPendingIntent A mutable {@link PendingIntent} that will be invoked when
+     *     data is requested. See {@link #getDataRequestFromIntent(Intent)}. Activities are not
+     *     allowed to be launched using this PendingIntent.
+     * @param executor Executor on which to run the consumer callback.
+     * @param statusConsumer A consumer that handles the status code for the observer registration.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    public void registerDataRequestObserver(
+            int dataType,
+            @NonNull PendingIntent dataRequestPendingIntent,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+        try {
+            RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
+            mService.registerDataRequestObserver(
+                    dataType, dataRequestPendingIntent, statusCallback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a previously registered data request observer. If the provided {@link
+     * PendingIntent} was not registered, or is already unregistered, the {@link
+     * WearableSensingService} will not be notified.
+     *
+     * @param dataType The data type the observer is for.
+     * @param dataRequestPendingIntent The observer to unregister.
+     * @param executor Executor on which to run the consumer callback.
+     * @param statusConsumer A consumer that handles the status code for the observer
+     *     unregistration.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+    public void unregisterDataRequestObserver(
+            int dataType,
+            @NonNull PendingIntent dataRequestPendingIntent,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+        try {
+            RemoteCallback statusCallback = createStatusCallback(executor, statusConsumer);
+            mService.unregisterDataRequestObserver(
+                    dataType, dataRequestPendingIntent, statusCallback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private static RemoteCallback createStatusCallback(
             Executor executor, Consumer<Integer> statusConsumer) {
         return new RemoteCallback(
diff --git a/core/java/android/app/wearable/flags.aconfig b/core/java/android/app/wearable/flags.aconfig
index 5e8bdb5..b4f628f 100644
--- a/core/java/android/app/wearable/flags.aconfig
+++ b/core/java/android/app/wearable/flags.aconfig
@@ -22,6 +22,13 @@
 }
 
 flag {
+    name: "enable_restart_wss_process"
+    namespace: "machine_learning"
+    description: "When this flag is on, the system will restart the WearableSensingService process before providing it with a new secure wearable connection."
+    bug: "301427767"
+}
+
+flag {
     name: "enable_hotword_wearable_sensing_api"
     namespace: "machine_learning"
     description: "This flag enables the APIs related to hotword in WearableSensingManager and WearableSensingService."
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 9fe8af5..a64ee5b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -239,6 +239,110 @@
     public String requiredDisplayCategory;
 
     /**
+     * Constant corresponding to {@code none} in the
+     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     * @hide
+     */
+    public static final int CONTENT_URI_PERMISSION_NONE = 0;
+
+    /**
+     * Constant corresponding to {@code read} in the
+     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     * @hide
+     */
+    public static final int CONTENT_URI_PERMISSION_READ = 1;
+
+    /**
+     * Constant corresponding to {@code write} in the
+     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     * @hide
+     */
+    public static final int CONTENT_URI_PERMISSION_WRITE = 2;
+
+    /**
+     * Constant corresponding to {@code readOrWrite} in the
+     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     * @hide
+     */
+    public static final int CONTENT_URI_PERMISSION_READ_OR_WRITE = 3;
+
+    /**
+     * Constant corresponding to {@code readAndWrite} in the
+     * {@link android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     * @hide
+     */
+    public static final int CONTENT_URI_PERMISSION_READ_AND_WRITE = 4;
+
+    /** @hide */
+    @SuppressWarnings("SwitchIntDef")
+    public static boolean isRequiredContentUriPermissionRead(
+            @RequiredContentUriPermission int permission) {
+        return switch (permission) {
+            case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE,
+                    CONTENT_URI_PERMISSION_READ -> true;
+            default -> false;
+        };
+    }
+
+    /** @hide */
+    @SuppressWarnings("SwitchIntDef")
+    public static boolean isRequiredContentUriPermissionWrite(
+            @RequiredContentUriPermission int permission) {
+        return switch (permission) {
+            case CONTENT_URI_PERMISSION_READ_AND_WRITE, CONTENT_URI_PERMISSION_READ_OR_WRITE,
+                    CONTENT_URI_PERMISSION_WRITE -> true;
+            default -> false;
+        };
+    }
+
+    /** @hide */
+    @IntDef(prefix = "CONTENT_URI_PERMISSION_", value = {
+            CONTENT_URI_PERMISSION_NONE,
+            CONTENT_URI_PERMISSION_READ,
+            CONTENT_URI_PERMISSION_WRITE,
+            CONTENT_URI_PERMISSION_READ_OR_WRITE,
+            CONTENT_URI_PERMISSION_READ_AND_WRITE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RequiredContentUriPermission {
+    }
+
+    private String requiredContentUriPermissionToFullString(
+            @RequiredContentUriPermission int permission) {
+        return switch (permission) {
+            case CONTENT_URI_PERMISSION_NONE -> "CONTENT_URI_PERMISSION_NONE";
+            case CONTENT_URI_PERMISSION_READ -> "CONTENT_URI_PERMISSION_READ";
+            case CONTENT_URI_PERMISSION_WRITE -> "CONTENT_URI_PERMISSION_WRITE";
+            case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "CONTENT_URI_PERMISSION_READ_OR_WRITE";
+            case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "CONTENT_URI_PERMISSION_READ_AND_WRITE";
+            default -> "unknown=" + permission;
+        };
+    }
+
+    /** @hide */
+    public static String requiredContentUriPermissionToShortString(
+            @RequiredContentUriPermission int permission) {
+        return switch (permission) {
+            case CONTENT_URI_PERMISSION_NONE -> "none";
+            case CONTENT_URI_PERMISSION_READ -> "read";
+            case CONTENT_URI_PERMISSION_WRITE -> "write";
+            case CONTENT_URI_PERMISSION_READ_OR_WRITE -> "read or write";
+            case CONTENT_URI_PERMISSION_READ_AND_WRITE -> "read and write";
+            default -> "unknown=" + permission;
+        };
+    }
+
+    /**
+     * Specifies permissions necessary to launch this activity via
+     * {@link android.content.Context#startActivity} when passing content URIs. The default value is
+     * {@code none}, meaning no specific permissions are required. Setting this attribute restricts
+     * activity invocation based on the invoker's permissions.
+     * @hide
+     */
+    @RequiredContentUriPermission
+    public int requireContentUriPermissionFromCaller;
+
+    /**
      * Activity can not be resized and always occupies the fullscreen area with all windows fully
      * visible.
      * @hide
@@ -1590,6 +1694,7 @@
         mMinAspectRatio = orig.mMinAspectRatio;
         supportsSizeChanges = orig.supportsSizeChanges;
         requiredDisplayCategory = orig.requiredDisplayCategory;
+        requireContentUriPermissionFromCaller = orig.requireContentUriPermissionFromCaller;
     }
 
     /**
@@ -1946,6 +2051,11 @@
         if (requiredDisplayCategory != null) {
             pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory);
         }
+        if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "requireContentUriPermissionFromCaller="
+                    + requiredContentUriPermissionToFullString(
+                            requireContentUriPermissionFromCaller));
+        }
         super.dumpBack(pw, prefix, dumpFlags);
     }
 
@@ -1993,6 +2103,7 @@
         dest.writeBoolean(supportsSizeChanges);
         sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
         dest.writeString8(requiredDisplayCategory);
+        dest.writeInt(requireContentUriPermissionFromCaller);
     }
 
     /**
@@ -2119,6 +2230,7 @@
             mKnownActivityEmbeddingCerts = null;
         }
         requiredDisplayCategory = source.readString8();
+        requireContentUriPermissionFromCaller = source.readInt();
     }
 
     /**
diff --git a/core/java/android/content/pm/IBackgroundInstallControlService.aidl b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
index c8e7cae..2e7f19e 100644
--- a/core/java/android/content/pm/IBackgroundInstallControlService.aidl
+++ b/core/java/android/content/pm/IBackgroundInstallControlService.aidl
@@ -17,10 +17,15 @@
 package android.content.pm;
 
 import android.content.pm.ParceledListSlice;
+import android.os.IRemoteCallback;
 
 /**
  * {@hide}
  */
 interface IBackgroundInstallControlService {
     ParceledListSlice getBackgroundInstalledPackages(long flags, int userId);
+
+    void registerBackgroundInstallCallback(IRemoteCallback callback);
+
+    void unregisterBackgroundInstallCallback(IRemoteCallback callback);
 }
diff --git a/core/java/android/credentials/selection/CancelSelectionRequest.java b/core/java/android/credentials/selection/CancelSelectionRequest.java
index 2662d76..55acfdb 100644
--- a/core/java/android/credentials/selection/CancelSelectionRequest.java
+++ b/core/java/android/credentials/selection/CancelSelectionRequest.java
@@ -21,7 +21,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -59,7 +58,7 @@
     private final boolean mShouldShowCancellationExplanation;
 
     @NonNull
-    private final String mAppPackageName;
+    private final String mPackageName;
 
     /**
      * Returns the request token matching the user request that should be cancelled.
@@ -85,8 +84,8 @@
      * metadata (e.g. "Cancelled by `App Name`").
      */
     @NonNull
-    public String getAppPackageName() {
-        return mAppPackageName;
+    public String getPackageName() {
+        return mPackageName;
     }
 
     /**
@@ -98,33 +97,36 @@
         return mShouldShowCancellationExplanation;
     }
 
+
     /**
      * Constructs a {@link CancelSelectionRequest}.
      *
-     * @hide
+     * @param requestToken request token matching the app request that should be cancelled
+     * @param shouldShowCancellationExplanation whether the UI should display some informational
+     *                                          cancellation message before closing
+     * @param packageName package that is invoking this request
+     *
      */
-    @TestApi
-    @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED)
-    public CancelSelectionRequest(@NonNull IBinder token, boolean shouldShowCancellationExplanation,
-            @NonNull String appPackageName) {
-        mToken = token;
+    public CancelSelectionRequest(@NonNull RequestToken requestToken,
+            boolean shouldShowCancellationExplanation, @NonNull String packageName) {
+        mToken = requestToken.getToken();
         mShouldShowCancellationExplanation = shouldShowCancellationExplanation;
-        mAppPackageName = appPackageName;
+        mPackageName = packageName;
     }
 
     private CancelSelectionRequest(@NonNull Parcel in) {
         mToken = in.readStrongBinder();
         AnnotationValidations.validate(NonNull.class, null, mToken);
         mShouldShowCancellationExplanation = in.readBoolean();
-        mAppPackageName = in.readString8();
-        AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
+        mPackageName = in.readString8();
+        AnnotationValidations.validate(NonNull.class, null, mPackageName);
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mToken);
         dest.writeBoolean(mShouldShowCancellationExplanation);
-        dest.writeString8(mAppPackageName);
+        dest.writeString8(mPackageName);
     }
 
     @Override
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index 1837976..ac2bae4 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -210,7 +210,8 @@
                                                 .config_credentialManagerDialogComponent));
         intent.setComponent(componentName);
         intent.putExtra(CancelSelectionRequest.EXTRA_CANCEL_UI_REQUEST,
-                new CancelSelectionRequest(requestToken, shouldShowCancellationUi, appPackageName));
+                new CancelSelectionRequest(new RequestToken(requestToken), shouldShowCancellationUi,
+                        appPackageName));
         return intent;
     }
 
diff --git a/core/java/android/credentials/selection/RequestInfo.java b/core/java/android/credentials/selection/RequestInfo.java
index 60bbae6..16d0802 100644
--- a/core/java/android/credentials/selection/RequestInfo.java
+++ b/core/java/android/credentials/selection/RequestInfo.java
@@ -106,7 +106,7 @@
     private final String mType;
 
     @NonNull
-    private final String mAppPackageName;
+    private final String mPackageName;
 
     private final boolean mHasPermissionToOverrideDefault;
 
@@ -172,8 +172,8 @@
 
     /** Returns the display name of the app that made this request. */
     @NonNull
-    public String getAppPackageName() {
-        return mAppPackageName;
+    public String getPackageName() {
+        return mPackageName;
     }
 
     /**
@@ -248,7 +248,7 @@
             boolean isShowAllOptionsRequested) {
         mToken = token;
         mType = type;
-        mAppPackageName = appPackageName;
+        mPackageName = appPackageName;
         mCreateCredentialRequest = createCredentialRequest;
         mGetCredentialRequest = getCredentialRequest;
         mHasPermissionToOverrideDefault = hasPermissionToOverrideDefault;
@@ -270,8 +270,8 @@
         AnnotationValidations.validate(NonNull.class, null, mToken);
         mType = type;
         AnnotationValidations.validate(NonNull.class, null, mType);
-        mAppPackageName = appPackageName;
-        AnnotationValidations.validate(NonNull.class, null, mAppPackageName);
+        mPackageName = appPackageName;
+        AnnotationValidations.validate(NonNull.class, null, mPackageName);
         mCreateCredentialRequest = createCredentialRequest;
         mGetCredentialRequest = getCredentialRequest;
         mHasPermissionToOverrideDefault = in.readBoolean();
@@ -284,7 +284,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mToken);
         dest.writeString8(mType);
-        dest.writeString8(mAppPackageName);
+        dest.writeString8(mPackageName);
         dest.writeTypedObject(mCreateCredentialRequest, flags);
         dest.writeTypedObject(mGetCredentialRequest, flags);
         dest.writeBoolean(mHasPermissionToOverrideDefault);
diff --git a/core/java/android/credentials/selection/RequestToken.java b/core/java/android/credentials/selection/RequestToken.java
index 27b83f8..f1953ce 100644
--- a/core/java/android/credentials/selection/RequestToken.java
+++ b/core/java/android/credentials/selection/RequestToken.java
@@ -30,6 +30,11 @@
  * To compare if two requests pertain to the same session, compare their RequestTokens using
  * the {@link RequestToken#equals(Object)} method.
  *
+ * For example, when receiving a {@link android.credentials.selection.CancelSelectionRequest},
+ * the developer should use {@link RequestToken#getToken()} to retrieve the token from request and
+ * compare whether it is equal with the cached token using {@link RequestToken#equals(Object)}. Only
+ * cancel the request when two tokens are the same.
+ *
  * @hide
  */
 @SystemApi
@@ -39,6 +44,12 @@
     @NonNull
     private final IBinder mToken;
 
+    /** @hide **/
+    @NonNull
+    public IBinder getToken() {
+        return mToken;
+    }
+
     /** @hide */
     @TestApi
     @FlaggedApi(FLAG_CONFIGURABLE_SELECTOR_UI_ENABLED)
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 54e34ec..62473c5 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -76,6 +76,12 @@
      */
     public static final int MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS = 5000;
 
+    /**
+     * Default value for {@link Settings.Secure#STYLUS_POINTER_ICON_ENABLED}.
+     * @hide
+     */
+    public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
+
     private InputSettings() {
     }
 
@@ -383,14 +389,19 @@
     }
 
     /**
-     * Whether a pointer icon will be shown over the location of a
-     * stylus pointer.
+     * Whether a pointer icon will be shown over the location of a stylus pointer.
+     *
      * @hide
      */
     public static boolean isStylusPointerIconEnabled(@NonNull Context context) {
+        if (InputProperties.force_enable_stylus_pointer_icon().orElse(false)) {
+            return true;
+        }
         return context.getResources()
-                       .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
-               || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+                        .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
+                && Settings.Secure.getIntForUser(context.getContentResolver(),
+                        Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
+                        DEFAULT_STYLUS_POINTER_ICON_ENABLED, UserHandle.USER_CURRENT_OR_SELF) != 0;
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ef2d5eb..ce7a026 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12312,6 +12312,16 @@
         public static final String PRIVATE_SPACE_AUTO_LOCK = "private_space_auto_lock";
 
         /**
+         * Toggle for enabling stylus pointer icon. Pointer icons for styluses will only be be shown
+         * when this is enabled. Enabling this alone won't enable the stylus pointer;
+         * config_enableStylusPointerIcon needs to be true as well.
+         *
+         * @hide
+         */
+        @Readable
+        public static final String STYLUS_POINTER_ICON_ENABLED = "stylus_pointer_icon_enabled";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS
index ec44100..763c79e 100644
--- a/core/java/android/service/voice/OWNERS
+++ b/core/java/android/service/voice/OWNERS
@@ -4,4 +4,4 @@
 
 # The owner here should not be assist owner
 liangyuchen@google.com
-tuanng@google.com
+adudani@google.com
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
index 8fdb2c2..0556188 100644
--- a/core/java/android/service/wearable/IWearableSensingService.aidl
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -31,6 +31,8 @@
     void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
     void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
     void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
+    void registerDataRequestObserver(int dataType, in RemoteCallback dataRequestCallback, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
+    void unregisterDataRequestObserver(int dataType, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
     void startDetection(in AmbientContextEventRequest request, in String packageName,
             in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback);
     void stopDetection(in String packageName);
diff --git a/core/java/android/service/wearable/WearableSensingDataRequester.java b/core/java/android/service/wearable/WearableSensingDataRequester.java
new file mode 100644
index 0000000..5a8104f
--- /dev/null
+++ b/core/java/android/service/wearable/WearableSensingDataRequester.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wearable;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.wearable.Flags;
+import android.app.wearable.WearableSensingDataRequest;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.function.Consumer;
+
+/**
+ * An interface to request wearable sensing data.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+@SystemApi
+public interface WearableSensingDataRequester {
+
+    /** An unknown status. */
+    int STATUS_UNKNOWN = 0;
+
+    /** The value of the status code that indicates success. */
+    int STATUS_SUCCESS = 1;
+
+    /**
+     * The value of the status code that indicates the request is rejected because the data request
+     * observer PendingIntent has been cancelled.
+     */
+    int STATUS_OBSERVER_CANCELLED = 2;
+
+    /**
+     * The value of the status code that indicates the request is rejected because it is larger than
+     * {@link WearableSensingDataRequest#getMaxRequestSize()}.
+     */
+    int STATUS_TOO_LARGE = 3;
+
+    /**
+     * The value of the status code that indicates the request is rejected because it exceeds the
+     * rate limit. See {@link WearableSensingDataRequest#getRateLimit()}.
+     */
+    int STATUS_TOO_FREQUENT = 4;
+
+    /** @hide */
+    @IntDef(
+            prefix = {"STATUS_"},
+            value = {
+                STATUS_UNKNOWN,
+                STATUS_SUCCESS,
+                STATUS_OBSERVER_CANCELLED,
+                STATUS_TOO_LARGE,
+                STATUS_TOO_FREQUENT
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface StatusCode {}
+
+    /**
+     * Sends a data request. See {@link WearableSensingService#onDataRequestObserverRegistered(int,
+     * String, WearableSensingDataRequester, Consumer)} for size and rate restrictions on data
+     * requests.
+     *
+     * @param dataRequest The data request to send.
+     * @param statusConsumer A consumer that handles the status code for the data request.
+     */
+    void requestData(
+            @NonNull WearableSensingDataRequest dataRequest,
+            @NonNull @StatusCode Consumer<Integer> statusConsumer);
+}
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index d21115b..d25cff7 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -25,6 +25,7 @@
 import android.app.ambientcontext.AmbientContextEvent;
 import android.app.ambientcontext.AmbientContextEventRequest;
 import android.app.wearable.Flags;
+import android.app.wearable.WearableSensingDataRequest;
 import android.app.wearable.WearableSensingManager;
 import android.content.Intent;
 import android.os.Bundle;
@@ -36,6 +37,7 @@
 import android.service.ambientcontext.AmbientContextDetectionResult;
 import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -90,6 +92,9 @@
     public static final String SERVICE_INTERFACE =
             "android.service.wearable.WearableSensingService";
 
+    private final SparseArray<WearableSensingDataRequester> mDataRequestObserverIdToRequesterMap =
+            new SparseArray<>();
+
     @Nullable
     @Override
     public final IBinder onBind(@NonNull Intent intent) {
@@ -128,6 +133,55 @@
 
                 /** {@inheritDoc} */
                 @Override
+                public void registerDataRequestObserver(
+                        int dataType,
+                        RemoteCallback dataRequestCallback,
+                        int dataRequestObserverId,
+                        String packageName,
+                        RemoteCallback statusCallback) {
+                    Objects.requireNonNull(dataRequestCallback);
+                    Objects.requireNonNull(statusCallback);
+                    WearableSensingDataRequester dataRequester;
+                    synchronized (mDataRequestObserverIdToRequesterMap) {
+                        dataRequester =
+                                mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId);
+                        if (dataRequester == null) {
+                            dataRequester = createDataRequester(dataRequestCallback);
+                            mDataRequestObserverIdToRequesterMap.put(
+                                    dataRequestObserverId, dataRequester);
+                        }
+                    }
+                    Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback);
+                    WearableSensingService.this.onDataRequestObserverRegistered(
+                            dataType, packageName, dataRequester, statusConsumer);
+                }
+
+                @Override
+                public void unregisterDataRequestObserver(
+                        int dataType,
+                        int dataRequestObserverId,
+                        String packageName,
+                        RemoteCallback statusCallback) {
+                    WearableSensingDataRequester dataRequester;
+                    synchronized (mDataRequestObserverIdToRequesterMap) {
+                        dataRequester =
+                                mDataRequestObserverIdToRequesterMap.get(dataRequestObserverId);
+                        if (dataRequester == null) {
+                            Slog.w(
+                                    TAG,
+                                    "dataRequestObserverId not found, cannot unregister data"
+                                            + " request observer.");
+                            return;
+                        }
+                        mDataRequestObserverIdToRequesterMap.remove(dataRequestObserverId);
+                    }
+                    Consumer<Integer> statusConsumer = createWearableStatusConsumer(statusCallback);
+                    WearableSensingService.this.onDataRequestObserverUnregistered(
+                            dataType, packageName, dataRequester, statusConsumer);
+                }
+
+                /** {@inheritDoc} */
+                @Override
                 public void startDetection(
                         @NonNull AmbientContextEventRequest request,
                         String packageName,
@@ -231,19 +285,19 @@
             @NonNull Consumer<Integer> statusConsumer);
 
     /**
-     * Called when configurations and read-only data in a {@link PersistableBundle}
-     * can be used by the WearableSensingService and sends the result to the {@link Consumer}
-     * right after the call. It is dependent on the application to define the type of data to
-     * provide. This is used by applications that will also provide an implementation of an isolated
-     * WearableSensingService. If the data was provided successfully
-     * {@link WearableSensingManager#STATUS_SUCCESS} will be provided.
+     * Called when configurations and read-only data in a {@link PersistableBundle} can be used by
+     * the WearableSensingService and sends the result to the {@link Consumer} right after the call.
+     * It is dependent on the application to define the type of data to provide. This is used by
+     * applications that will also provide an implementation of an isolated WearableSensingService.
+     * If the data was provided successfully {@link WearableSensingManager#STATUS_SUCCESS} will be
+     * provided.
      *
      * @param data Application configuration data to provide to the {@link WearableSensingService}.
-     *             PersistableBundle does not allow any remotable objects or other contents
-     *             that can be used to communicate with other processes.
-     * @param sharedMemory The unrestricted data blob to
-     *                     provide to the {@link WearableSensingService}. Use this to provide the
-     *                     sensing models data or other such data to the trusted process.
+     *     PersistableBundle does not allow any remotable objects or other contents that can be used
+     *     to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob to provide to the {@link
+     *     WearableSensingService}. Use this to provide the sensing models data or other such data
+     *     to the trusted process.
      * @param statusConsumer the consumer for the service status.
      */
     @BinderThread
@@ -253,6 +307,68 @@
             @NonNull Consumer<Integer> statusConsumer);
 
     /**
+     * Called when a data request observer is registered. Each request must not be larger than
+     * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link
+     * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link
+     * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too
+     * frequent will be dropped by the system. See {@link
+     * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details
+     * about the status code returned for each request.
+     *
+     * <p>The implementing class should override this method. After the data requester is received,
+     * it should send a {@link WearableSensingManager#STATUS_SUCCESS} status code to the {@code
+     * statusConsumer} unless it encounters an error condition described by a status code listed in
+     * {@link WearableSensingManager}, such as {@link
+     * WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE}, in which case it should return the
+     * corresponding status code.
+     *
+     * @param dataType The data type the observer is registered for. Values are defined by the
+     *     application that implements this class.
+     * @param packageName The package name of the app that will receive the requests.
+     * @param dataRequester A handle to the observer registered. It can be used to request data of
+     *     the specified data type.
+     * @param statusConsumer the consumer for the status of the data request observer registration.
+     *     This is different from the status for each data request.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    @BinderThread
+    public void onDataRequestObserverRegistered(
+            int dataType,
+            @NonNull String packageName,
+            @NonNull WearableSensingDataRequester dataRequester,
+            @NonNull Consumer<Integer> statusConsumer) {
+        statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+    }
+
+    /**
+     * Called when a data request observer is unregistered.
+     *
+     * <p>The implementing class should override this method. It should send a {@link
+     * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
+     * encounters an error condition described by a status code listed in {@link
+     * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
+     * in which case it should return the corresponding status code.
+     *
+     * @param dataType The data type the observer is for.
+     * @param packageName The package name of the app that will receive the requests sent to the
+     *     dataRequester.
+     * @param dataRequester A handle to the observer to be unregistered. It is the exact same
+     *     instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String,
+     *     WearableSensingDataRequester, Consumer)} invocation.
+     * @param statusConsumer the consumer for the status of the data request observer
+     *     unregistration. This is different from the status for each data request.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_DATA_REQUEST_OBSERVER_API)
+    @BinderThread
+    public void onDataRequestObserverUnregistered(
+            int dataType,
+            @NonNull String packageName,
+            @NonNull WearableSensingDataRequester dataRequester,
+            @NonNull Consumer<Integer> statusConsumer) {
+        statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+    }
+
+    /**
      * Called when a client app requests starting detection of the events in the request. The
      * implementation should keep track of whether the user has explicitly consented to detecting
      * the events using on-going ambient sensor (e.g. microphone), and agreed to share the
@@ -309,6 +425,25 @@
         return intArray;
     }
 
+    private static WearableSensingDataRequester createDataRequester(
+            RemoteCallback dataRequestCallback) {
+        return (request, requestStatusConsumer) -> {
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(WearableSensingDataRequest.REQUEST_BUNDLE_KEY, request);
+            RemoteCallback requestStatusCallback =
+                    new RemoteCallback(
+                            requestStatusBundle -> {
+                                requestStatusConsumer.accept(
+                                        requestStatusBundle.getInt(
+                                                WearableSensingManager.STATUS_RESPONSE_BUNDLE_KEY));
+                            });
+            bundle.putParcelable(
+                    WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY,
+                    requestStatusCallback);
+            dataRequestCallback.sendResult(bundle);
+        };
+    }
+
     @NonNull
     private static Consumer<Integer> createWearableStatusConsumer(RemoteCallback statusCallback) {
         return response -> {
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 9b21b76..270bf8b 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -173,6 +174,8 @@
     private Bitmap mBitmapFrames[];
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int mDurationPerFrame;
+    @SuppressWarnings("unused")
+    private boolean mDrawNativeDropShadow;
 
     private PointerIcon(int type) {
         mType = type;
@@ -231,9 +234,15 @@
             typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT);
         }
 
-        final int defStyle = useLargeIcons
-                ? com.android.internal.R.style.LargePointer
-                : com.android.internal.R.style.Pointer;
+        final int defStyle;
+        // TODO(b/305193969): Use scaled vectors when large icons are requested.
+        if (useLargeIcons) {
+            defStyle = com.android.internal.R.style.LargePointer;
+        } else if (android.view.flags.Flags.enableVectorCursors()) {
+            defStyle = com.android.internal.R.style.VectorPointer;
+        } else {
+            defStyle = com.android.internal.R.style.Pointer;
+        }
         TypedArray a = context.obtainStyledAttributes(null,
                 com.android.internal.R.styleable.Pointer,
                 0, defStyle);
@@ -333,6 +342,7 @@
                                     Bitmap.CREATOR.createFromParcel(in),
                                     in.readFloat(),
                                     in.readFloat());
+                    icon.mDrawNativeDropShadow = in.readBoolean();
                     return icon;
                 }
 
@@ -362,6 +372,7 @@
         mBitmap.writeToParcel(out, flags);
         out.writeFloat(mHotSpotX);
         out.writeFloat(mHotSpotY);
+        out.writeBoolean(mDrawNativeDropShadow);
     }
 
     @Override
@@ -415,6 +426,16 @@
         return scaled;
     }
 
+    private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
+            VectorDrawable vectorDrawable) {
+        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
+                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        vectorDrawable.draw(canvas);
+        return new BitmapDrawable(resources, bitmap);
+    }
+
     private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
         final XmlResourceParser parser = resources.getXml(resourceId);
         final int bitmapRes;
@@ -476,6 +497,10 @@
                 }
             }
         }
+        if (drawable instanceof VectorDrawable) {
+            mDrawNativeDropShadow = true;
+            drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable);
+        }
         if (!(drawable instanceof BitmapDrawable)) {
             throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
                     + "refer to a bitmap drawable.");
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index 1dd99ba..3e7a9cb 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -1,13 +1,6 @@
 package: "android.view.flags"
 
 flag {
-     name: "enable_surface_native_alloc_registration"
-     namespace: "toolkit"
-     description: "Feature flag for registering surfaces with the VM for faster cleanup"
-     bug: "306193257"
-}
-
-flag {
     name: "enable_surface_native_alloc_registration_ro"
     namespace: "toolkit"
     description: "Feature flag for registering surfaces with the VM for faster"
@@ -24,3 +17,11 @@
     bug: "316170253"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_vector_cursors"
+    namespace: "systemui"
+    description: "Feature flag to enable vector drawables in addition to bitmaps for PointerIcon."
+    bug: "305193969"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/view/flags/window_insets.aconfig b/core/java/android/view/flags/window_insets.aconfig
new file mode 100644
index 0000000..201b7ad
--- /dev/null
+++ b/core/java/android/view/flags/window_insets.aconfig
@@ -0,0 +1,9 @@
+package: "android.view.flags"
+
+flag {
+    name: "customizable_window_headers"
+    namespace: "lse_desktop_experience"
+    description: "Flag to control the caption bar appearance and to fit app content in its empty space"
+    bug: "316387515"
+    is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
index b0f3578..a051c1b 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
@@ -89,4 +89,7 @@
      */
     @Nullable
     String getRequiredDisplayCategory();
+
+    /** Gets the permissions necessary for launching the activity when using content URIs. */
+    int getRequireContentUriPermissionFromCaller();
 }
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
index 2f977ee..1218793 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java
@@ -36,9 +36,9 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
-import com.android.internal.pm.pkg.parsing.ParsingUtils;
 
 import java.util.Collections;
 import java.util.Locale;
@@ -97,6 +97,8 @@
     @Nullable
     private String mRequiredDisplayCategory;
 
+    private int mRequireContentUriPermissionFromCaller;
+
     public ParsedActivityImpl(ParsedActivityImpl other) {
         super(other);
         this.theme = other.theme;
@@ -124,6 +126,7 @@
         this.windowLayout = other.windowLayout;
         this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
         this.mRequiredDisplayCategory = other.mRequiredDisplayCategory;
+        this.mRequireContentUriPermissionFromCaller = other.mRequireContentUriPermissionFromCaller;
     }
 
     /**
@@ -192,6 +195,8 @@
         alias.setDirectBootAware(target.isDirectBootAware());
         alias.setProcessName(target.getProcessName());
         alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory());
+        alias.setRequireContentUriPermissionFromCaller(
+                target.getRequireContentUriPermissionFromCaller());
         return alias;
 
         // Not all attributes from the target ParsedActivity are copied to the alias.
@@ -320,6 +325,7 @@
         }
         sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
         dest.writeString8(this.mRequiredDisplayCategory);
+        dest.writeInt(this.mRequireContentUriPermissionFromCaller);
     }
 
     public ParsedActivityImpl() {
@@ -355,6 +361,7 @@
         }
         this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
         this.mRequiredDisplayCategory = in.readString8();
+        this.mRequireContentUriPermissionFromCaller = in.readInt();
     }
 
     @NonNull
@@ -412,7 +419,8 @@
             int rotationAnimation,
             int colorMode,
             @Nullable ActivityInfo.WindowLayout windowLayout,
-            @Nullable String requiredDisplayCategory) {
+            @Nullable String requiredDisplayCategory,
+            int requireContentUriPermissionFromCaller) {
         this.theme = theme;
         this.uiOptions = uiOptions;
         this.targetActivity = targetActivity;
@@ -438,6 +446,7 @@
         this.colorMode = colorMode;
         this.windowLayout = windowLayout;
         this.mRequiredDisplayCategory = requiredDisplayCategory;
+        this.mRequireContentUriPermissionFromCaller = requireContentUriPermissionFromCaller;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -563,6 +572,11 @@
     }
 
     @DataClass.Generated.Member
+    public int getRequireContentUriPermissionFromCaller() {
+        return mRequireContentUriPermissionFromCaller;
+    }
+
+    @DataClass.Generated.Member
     public @NonNull ParsedActivityImpl setTheme( int value) {
         theme = value;
         return this;
@@ -694,11 +708,17 @@
         return this;
     }
 
+    @DataClass.Generated.Member
+    public @NonNull ParsedActivityImpl setRequireContentUriPermissionFromCaller( int value) {
+        mRequireContentUriPermissionFromCaller = value;
+        return this;
+    }
+
     @DataClass.Generated(
-            time = 1701338377709L,
+            time = 1706180262165L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java",
-            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\nprivate  int mRequireContentUriPermissionFromCaller\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
index c3f7dab..9f71d88 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
@@ -241,6 +241,10 @@
 
             activity.setRequiredDisplayCategory(requiredDisplayCategory);
 
+            activity.setRequireContentUriPermissionFromCaller(sa.getInt(
+                    R.styleable.AndroidManifestActivity_requireContentUriPermissionFromCaller,
+                    ActivityInfo.CONTENT_URI_PERMISSION_NONE));
+
             return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                     false /*isAlias*/, visibleToEphemeral, input,
                     R.styleable.AndroidManifestActivity_parentActivityName,
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index a8d0d37..889434f 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -168,12 +168,12 @@
     }
 
     public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
-                              @AttrRes int defStyleAttr) {
+            @AttrRes int defStyleAttr) {
         super(context, attrs, defStyleAttr);
     }
 
     public ConversationLayout(@NonNull Context context, @Nullable AttributeSet attrs,
-                              @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
 
@@ -432,8 +432,14 @@
         final List<MessagingMessage> newHistoricMessagingMessages =
                 createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText);
 
+        // Add our new MessagingMessages to groups
+        List<List<MessagingMessage>> groups = new ArrayList<>();
+        List<Person> senders = new ArrayList<>();
+        // Lets first find the groups (populate `groups` and `senders`)
+        findGroups(newHistoricMessagingMessages, newMessagingMessages, user, groups, senders);
+
         return new MessagingData(user, showSpinner, unreadCount,
-                newHistoricMessagingMessages, newMessagingMessages);
+                newHistoricMessagingMessages, newMessagingMessages, groups, senders);
     }
 
     /**
@@ -509,21 +515,13 @@
         setUser(messagingData.getUser());
         setUnreadCount(messagingData.getUnreadCount());
 
-        List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
-        List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
         // Copy our groups, before they get clobbered
         ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
 
-        // Add our new MessagingMessages to groups
-        List<List<MessagingMessage>> groups = new ArrayList<>();
-        List<Person> senders = new ArrayList<>();
-
-        // Lets first find the groups (populate `groups` and `senders`)
-        findGroups(historicMessages, messages, groups, senders);
-
         // Let's now create the views and reorder them accordingly
         //   side-effect: updates mGroups, mAddedGroups
-        createGroupViews(groups, senders, messagingData.getShowSpinner());
+        createGroupViews(messagingData.getGroups(), messagingData.getSenders(),
+                messagingData.getShowSpinner());
 
         // Let's first check which groups were removed altogether and remove them in one animation
         removeGroups(oldGroups);
@@ -536,8 +534,8 @@
             historicMessage.removeMessage(mToRecycle);
         }
 
-        mMessages = messages;
-        mHistoricMessages = historicMessages;
+        mMessages = messagingData.getNewMessagingMessages();
+        mHistoricMessages = messagingData.getHistoricMessagingMessages();
 
         updateHistoricMessageVisibility();
         updateTitleAndNamesDisplay();
@@ -935,7 +933,7 @@
     }
 
     private void createGroupViews(List<List<MessagingMessage>> groups,
-                                  List<Person> senders, boolean showSpinner) {
+            List<Person> senders, boolean showSpinner) {
         mGroups.clear();
         for (int groupIndex = 0; groupIndex < groups.size(); groupIndex++) {
             List<MessagingMessage> group = groups.get(groupIndex);
@@ -983,9 +981,12 @@
         }
     }
 
+    /**
+     * Finds groups and senders from the given messaging messages and fills outGroups and outSenders
+     */
     private void findGroups(List<MessagingMessage> historicMessages,
-                            List<MessagingMessage> messages, List<List<MessagingMessage>> groups,
-                            List<Person> senders) {
+            List<MessagingMessage> messages, Person user, List<List<MessagingMessage>> outGroups,
+            List<Person> outSenders) {
         CharSequence currentSenderKey = null;
         List<MessagingMessage> currentGroup = null;
         int histSize = historicMessages.size();
@@ -1003,14 +1004,14 @@
             isNewGroup |= !TextUtils.equals(key, currentSenderKey);
             if (isNewGroup) {
                 currentGroup = new ArrayList<>();
-                groups.add(currentGroup);
+                outGroups.add(currentGroup);
                 if (sender == null) {
-                    sender = mUser;
+                    sender = user;
                 } else {
                     // Remove all formatting from the sender name
                     sender = sender.toBuilder().setName(Objects.toString(sender.getName())).build();
                 }
-                senders.add(sender);
+                outSenders.add(sender);
                 currentSenderKey = key;
             }
             currentGroup.add(message);
diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java
index 85b0201..42de60e 100644
--- a/core/java/com/android/internal/widget/MessagingData.java
+++ b/core/java/com/android/internal/widget/MessagingData.java
@@ -28,24 +28,33 @@
     private final boolean mShowSpinner;
     private final List<MessagingMessage> mHistoricMessagingMessages;
     private final List<MessagingMessage> mNewMessagingMessages;
+    private final List<List<MessagingMessage>> mGroups;
+    private final List<Person> mSenders;
     private final int mUnreadCount;
 
     MessagingData(Person user, boolean showSpinner,
             List<MessagingMessage> historicMessagingMessages,
-            List<MessagingMessage> newMessagingMessages) {
+            List<MessagingMessage> newMessagingMessages, List<List<MessagingMessage>> groups,
+            List<Person> senders) {
         this(user, showSpinner, /* unreadCount= */0,
-                historicMessagingMessages, newMessagingMessages);
+                historicMessagingMessages, newMessagingMessages,
+                groups,
+                senders);
     }
 
     MessagingData(Person user, boolean showSpinner,
             int unreadCount,
             List<MessagingMessage> historicMessagingMessages,
-            List<MessagingMessage> newMessagingMessages) {
+            List<MessagingMessage> newMessagingMessages,
+            List<List<MessagingMessage>> groups,
+            List<Person> senders) {
         mUser = user;
         mShowSpinner = showSpinner;
         mUnreadCount = unreadCount;
         mHistoricMessagingMessages = historicMessagingMessages;
         mNewMessagingMessages = newMessagingMessages;
+        mGroups = groups;
+        mSenders = senders;
     }
 
     public Person getUser() {
@@ -67,4 +76,12 @@
     public int getUnreadCount() {
         return mUnreadCount;
     }
+
+    public List<Person> getSenders() {
+        return mSenders;
+    }
+
+    public List<List<MessagingMessage>> getGroups() {
+        return mGroups;
+    }
 }
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index b6d7503..d000596 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -189,9 +189,15 @@
                 /* isHistoric= */true, usePrecomputedText);
         final List<MessagingMessage> newMessagingMessages =
                 createMessages(newMessages, /* isHistoric */false, usePrecomputedText);
+        // Let's first find our groups!
+        List<List<MessagingMessage>> groups = new ArrayList<>();
+        List<Person> senders = new ArrayList<>();
+
+        // Lets first find the groups
+        findGroups(historicMessagingMessages, newMessagingMessages, groups, senders);
 
         return new MessagingData(user, showSpinner,
-                historicMessagingMessages, newMessagingMessages);
+                historicMessagingMessages, newMessagingMessages, groups, senders);
     }
 
     /**
@@ -256,10 +262,10 @@
     private void bind(MessagingData messagingData) {
         setUser(messagingData.getUser());
 
-        List<MessagingMessage> historicMessages = messagingData.getHistoricMessagingMessages();
-        List<MessagingMessage> messages = messagingData.getNewMessagingMessages();
+        // Let's now create the views and reorder them accordingly
         ArrayList<MessagingGroup> oldGroups = new ArrayList<>(mGroups);
-        addMessagesToGroups(historicMessages, messages, messagingData.getShowSpinner());
+        createGroupViews(messagingData.getGroups(), messagingData.getSenders(),
+                messagingData.getShowSpinner());
 
         // Let's first check which groups were removed altogether and remove them in one animation
         removeGroups(oldGroups);
@@ -272,8 +278,8 @@
             historicMessage.removeMessage(mToRecycle);
         }
 
-        mMessages = messages;
-        mHistoricMessages = historicMessages;
+        mMessages = messagingData.getNewMessagingMessages();
+        mHistoricMessages = messagingData.getHistoricMessagingMessages();
 
         updateHistoricMessageVisibility();
         updateTitleAndNamesDisplay();
@@ -451,19 +457,6 @@
         }
     }
 
-    private void addMessagesToGroups(List<MessagingMessage> historicMessages,
-            List<MessagingMessage> messages, boolean showSpinner) {
-        // Let's first find our groups!
-        List<List<MessagingMessage>> groups = new ArrayList<>();
-        List<Person> senders = new ArrayList<>();
-
-        // Lets first find the groups
-        findGroups(historicMessages, messages, groups, senders);
-
-        // Let's now create the views and reorder them accordingly
-        createGroupViews(groups, senders, showSpinner);
-    }
-
     private void createGroupViews(List<List<MessagingMessage>> groups,
             List<Person> senders, boolean showSpinner) {
         mGroups.clear();
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3fc1683..240028c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -98,6 +98,7 @@
         "libminikin",
         "libz",
         "server_configurable_flags",
+        "android.media.audiopolicy-aconfig-cc",
     ],
 
     static_libs: [
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 969e47b..070d07c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -22,6 +22,7 @@
 #include <android/media/INativeSpatializerCallback.h>
 #include <android/media/ISpatializer.h>
 #include <android/media/audio/common/AudioConfigBase.h>
+#include <android_media_audiopolicy.h>
 #include <android_os_Parcel.h>
 #include <audiomanager/AudioManager.h>
 #include <jni.h>
@@ -55,6 +56,8 @@
 
 // ----------------------------------------------------------------------------
 
+namespace audio_flags = android::media::audiopolicy;
+
 using namespace android;
 using media::audio::common::AudioConfigBase;
 
@@ -145,6 +148,7 @@
 } gAudioPatchFields;
 
 static jclass gAudioMixClass;
+static jmethodID gAudioMixCstor;
 static struct {
     jfieldID    mRule;
     jfieldID    mFormat;
@@ -165,7 +169,15 @@
     // other fields unused by JNI
 } gAudioFormatFields;
 
+static jclass gAudioAttributesClass;
+static jmethodID gAudioAttributesCstor;
+static struct {
+    jfieldID mSource;
+    jfieldID mUsage;
+} gAudioAttributesFields;
+
 static jclass gAudioMixingRuleClass;
+static jmethodID gAudioMixingRuleCstor;
 static struct {
     jfieldID    mCriteria;
     jfieldID    mAllowPrivilegedPlaybackCapture;
@@ -174,6 +186,8 @@
 } gAudioMixingRuleFields;
 
 static jclass gAudioMixMatchCriterionClass;
+static jmethodID gAudioMixMatchCriterionAttrCstor;
+static jmethodID gAudioMixMatchCriterionIntPropCstor;
 static struct {
     jfieldID    mAttr;
     jfieldID    mIntProp;
@@ -2087,6 +2101,39 @@
                           channelMask, channelIndexMask);
 }
 
+jint nativeAudioConfigToJavaAudioFormat(JNIEnv *env, const audio_config_t *nConfigBase,
+                                        jobject *jAudioFormat, bool isInput) {
+    if (!audio_flags::audio_mix_test_api()) {
+        return AUDIO_JAVA_INVALID_OPERATION;
+    }
+
+    if (nConfigBase == nullptr) {
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+    int propertyMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING | AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
+    int channelMask = 0;
+    int channelIndexMask = 0;
+    switch (audio_channel_mask_get_representation(nConfigBase->channel_mask)) {
+        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+            channelMask = isInput ? inChannelMaskFromNative(nConfigBase->channel_mask)
+                                  : outChannelMaskFromNative(nConfigBase->channel_mask);
+            propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
+            break;
+        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+            channelIndexMask = audio_channel_mask_get_bits(nConfigBase->channel_mask);
+            propertyMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK;
+            break;
+        default:
+            // This must not happen
+            break;
+    }
+
+    *jAudioFormat = env->NewObject(gAudioFormatClass, gAudioFormatCstor, propertyMask,
+                                   audioFormatFromNative(nConfigBase->format),
+                                   nConfigBase->sample_rate, channelMask, channelIndexMask);
+    return AUDIO_JAVA_SUCCESS;
+}
+
 jint convertAudioMixerAttributesToNative(JNIEnv *env, const jobject jAudioMixerAttributes,
                                          audio_mixer_attributes_t *nMixerAttributes) {
     ScopedLocalRef<jobject> jFormat(env,
@@ -2179,6 +2226,88 @@
     return AUDIO_JAVA_SUCCESS;
 }
 
+static jint nativeAudioMixToJavaAudioMixingRule(JNIEnv *env, const AudioMix &nAudioMix,
+                                                jobject *jAudioMixingRule) {
+    if (!audio_flags::audio_mix_test_api()) {
+        return AUDIO_JAVA_INVALID_OPERATION;
+    }
+
+    jobject jAudioMixMatchCriterionList = env->NewObject(gArrayListClass, gArrayListMethods.cstor);
+    for (const auto &criteria : nAudioMix.mCriteria) {
+        jobject jAudioAttributes = NULL;
+        jobject jMixMatchCriterion = NULL;
+        jobject jValueInteger = NULL;
+        switch (criteria.mRule) {
+            case RULE_MATCH_UID:
+                jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUid);
+                jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+                                                    gAudioMixMatchCriterionIntPropCstor,
+                                                    jValueInteger, criteria.mRule);
+                break;
+            case RULE_MATCH_USERID:
+                jValueInteger =
+                        env->NewObject(gIntegerClass, gIntegerCstor, criteria.mValue.mUserId);
+                jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+                                                    gAudioMixMatchCriterionIntPropCstor,
+                                                    jValueInteger, criteria.mRule);
+                break;
+            case RULE_MATCH_AUDIO_SESSION_ID:
+                jValueInteger = env->NewObject(gIntegerClass, gIntegerCstor,
+                                               criteria.mValue.mAudioSessionId);
+                jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+                                                    gAudioMixMatchCriterionIntPropCstor,
+                                                    jValueInteger, criteria.mRule);
+                break;
+            case RULE_MATCH_ATTRIBUTE_USAGE:
+                jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
+                env->SetIntField(jAudioAttributes, gAudioAttributesFields.mUsage,
+                                 criteria.mValue.mUsage);
+                jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+                                                    gAudioMixMatchCriterionAttrCstor,
+                                                    jMixMatchCriterion, criteria.mRule);
+                break;
+            case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+                jAudioAttributes = env->NewObject(gAudioAttributesClass, gAudioAttributesCstor);
+                env->SetIntField(jAudioAttributes, gAudioAttributesFields.mSource,
+                                 criteria.mValue.mSource);
+                jMixMatchCriterion = env->NewObject(gAudioMixMatchCriterionClass,
+                                                    gAudioMixMatchCriterionAttrCstor,
+                                                    jMixMatchCriterion, criteria.mRule);
+                break;
+        }
+        env->CallBooleanMethod(jAudioMixMatchCriterionList, gArrayListMethods.add,
+                               jMixMatchCriterion);
+    }
+
+    *jAudioMixingRule = env->NewObject(gAudioMixingRuleClass, gAudioMixingRuleCstor,
+                                       nAudioMix.mMixType, jAudioMixMatchCriterionList,
+                                       nAudioMix.mAllowPrivilegedMediaPlaybackCapture,
+                                       nAudioMix.mVoiceCommunicationCaptureAllowed);
+    return AUDIO_JAVA_SUCCESS;
+}
+
+static jint convertAudioMixFromNative(JNIEnv *env, jobject *jAudioMix, const AudioMix &nAudioMix) {
+    if (!audio_flags::audio_mix_test_api()) {
+        return AUDIO_JAVA_INVALID_OPERATION;
+    }
+    jobject jAudioMixingRule = NULL;
+    int status = nativeAudioMixToJavaAudioMixingRule(env, nAudioMix, &jAudioMixingRule);
+    if (status != AUDIO_JAVA_SUCCESS) {
+        return status;
+    }
+    jobject jAudioFormat = NULL;
+    status = nativeAudioConfigToJavaAudioFormat(env, &nAudioMix.mFormat, &jAudioFormat, false);
+    if (status != AUDIO_JAVA_SUCCESS) {
+        return status;
+    }
+
+    jstring deviceAddress = env->NewStringUTF(nAudioMix.mDeviceAddress.c_str());
+    *jAudioMix = env->NewObject(gAudioMixClass, gAudioMixCstor, jAudioMixingRule, jAudioFormat,
+                                nAudioMix.mRouteFlags, nAudioMix.mCbFlags, nAudioMix.mDeviceType,
+                                deviceAddress);
+    return AUDIO_JAVA_SUCCESS;
+}
+
 static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, const jobject jAudioMix) {
     nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType);
     nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags);
@@ -2252,6 +2381,34 @@
     return nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioSystem_getRegisteredPolicyMixes(JNIEnv *env, jobject clazz,
+                                                               jobject jMixes) {
+    if (!audio_flags::audio_mix_test_api()) {
+        return AUDIO_JAVA_INVALID_OPERATION;
+    }
+
+    status_t status;
+    std::vector<AudioMix> mixes;
+    ALOGV("AudioSystem::getRegisteredPolicyMixes");
+    status = AudioSystem::getRegisteredPolicyMixes(mixes);
+    ALOGV("AudioSystem::getRegisteredPolicyMixes() returned %zu mixes. Status=%d", mixes.size(),
+          status);
+    if (status != NO_ERROR) {
+        return nativeToJavaStatus(status);
+    }
+
+    for (const auto &mix : mixes) {
+        jobject jAudioMix = NULL;
+        int conversionStatus = convertAudioMixFromNative(env, &jAudioMix, mix);
+        if (conversionStatus != AUDIO_JAVA_SUCCESS) {
+            return conversionStatus;
+        }
+        env->CallBooleanMethod(jMixes, gListMethods.add, jAudioMix);
+    }
+
+    return AUDIO_JAVA_SUCCESS;
+}
+
 static jint android_media_AudioSystem_updatePolicyMixes(JNIEnv *env, jobject clazz,
                                                         jobjectArray mixes,
                                                         jobjectArray updatedMixingRules) {
@@ -3251,6 +3408,8 @@
          MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession),
          MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
                                 android_media_AudioSystem_registerPolicyMixes),
+         MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I",
+                                android_media_AudioSystem_getRegisteredPolicyMixes),
          MAKE_JNI_NATIVE_METHOD("updatePolicyMixes",
                                 "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/"
                                 "AudioMixingRule;)I",
@@ -3499,6 +3658,11 @@
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
+    if (audio_flags::audio_mix_test_api()) {
+        gAudioMixCstor = GetMethodIDOrDie(env, audioMixClass, "<init>",
+                                          "(Landroid/media/audiopolicy/AudioMixingRule;Landroid/"
+                                          "media/AudioFormat;IIILjava/lang/String;)V");
+    }
     gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule",
                                                 "Landroid/media/audiopolicy/AudioMixingRule;");
     gAudioMixFields.mFormat = GetFieldIDOrDie(env, audioMixClass, "mFormat",
@@ -3521,6 +3685,10 @@
 
     jclass audioMixingRuleClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule");
     gAudioMixingRuleClass = MakeGlobalRefOrDie(env, audioMixingRuleClass);
+    if (audio_flags::audio_mix_test_api()) {
+        gAudioMixingRuleCstor = GetMethodIDOrDie(env, audioMixingRuleClass, "<init>",
+                                                 "(ILjava/util/Collection;ZZ)V");
+    }
     gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria",
                                                        "Ljava/util/ArrayList;");
     gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture =
@@ -3529,9 +3697,24 @@
     gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed =
             GetFieldIDOrDie(env, audioMixingRuleClass, "mVoiceCommunicationCaptureAllowed", "Z");
 
+    if (audio_flags::audio_mix_test_api()) {
+        jclass audioAttributesClass = FindClassOrDie(env, "android/media/AudioAttributes");
+        gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
+        gAudioAttributesCstor = GetMethodIDOrDie(env, gAudioAttributesClass, "<init>", "()V");
+        gAudioAttributesFields.mSource = GetFieldIDOrDie(env, gAudioAttributesClass, "mUsage", "I");
+        gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, gAudioAttributesClass, "mSource", "I");
+    }
+
     jclass audioMixMatchCriterionClass =
                 FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
     gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
+    if (audio_flags::audio_mix_test_api()) {
+        gAudioMixMatchCriterionAttrCstor =
+                GetMethodIDOrDie(env, gAudioMixMatchCriterionClass, "<init>",
+                                 "(Landroid/media/AudioAttributes;I)V");
+        gAudioMixMatchCriterionIntPropCstor = GetMethodIDOrDie(env, gAudioMixMatchCriterionClass,
+                                                               "<init>", "(Ljava/lang/Integer;I)V");
+    }
     gAudioMixMatchCriterionFields.mAttr = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mAttr",
                                                        "Landroid/media/AudioAttributes;");
     gAudioMixMatchCriterionFields.mIntProp = GetFieldIDOrDie(env, audioMixMatchCriterionClass, "mIntProp",
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index c6a3b52..86b0009 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -37,6 +37,7 @@
     jfieldID mHotSpotY;
     jfieldID mBitmapFrames;
     jfieldID mDurationPerFrame;
+    jfieldID mDrawNativeDropShadow;
 } gPointerIconClassInfo;
 
 
@@ -51,6 +52,8 @@
             env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType));
     icon.hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
     icon.hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
+    icon.drawNativeDropShadow =
+            env->GetBooleanField(pointerIconObj, gPointerIconClassInfo.mDrawNativeDropShadow);
 
     ScopedLocalRef<jobject> bitmapObj(
             env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
@@ -95,6 +98,9 @@
     gPointerIconClassInfo.mBitmapFrames = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz,
             "mBitmapFrames", "[Landroid/graphics/Bitmap;");
 
+    gPointerIconClassInfo.mDrawNativeDropShadow =
+            GetFieldIDOrDie(env, gPointerIconClassInfo.clazz, "mDrawNativeDropShadow", "Z");
+
     gPointerIconClassInfo.mDurationPerFrame = GetFieldIDOrDie(env, gPointerIconClassInfo.clazz,
             "mDurationPerFrame", "I");
 
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index ee446fb..1b6a397 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -39,6 +39,7 @@
     float hotSpotY;
     std::vector<graphics::Bitmap> bitmapFrames;
     int32_t durationPerFrame;
+    bool drawNativeDropShadow;
 
     inline bool isNullIcon() { return style == PointerIconStyle::TYPE_NULL; }
 
@@ -49,6 +50,7 @@
         hotSpotY = 0;
         bitmapFrames.clear();
         durationPerFrame = 0;
+        drawNativeDropShadow = false;
     }
 };
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 58166bf..0938ce17 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2416,6 +2416,11 @@
                          const std::vector<int>& fds_to_ignore,
                          bool is_priority_fork,
                          bool purge) {
+  ATRACE_CALL();
+  if (is_priority_fork) {
+    setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+  }
+
   SetSignalHandlers();
 
   // Curry a failure function.
@@ -2501,6 +2506,10 @@
   // We blocked SIGCHLD prior to a fork, we unblock it here.
   UnblockSignal(SIGCHLD, fail_fn);
 
+  if (is_priority_fork && pid != 0) {
+    setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
+  }
+
   return pid;
 }
 
@@ -2570,6 +2579,7 @@
         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
         jlong effective_capabilities) {
+  ATRACE_CALL();
   std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
                    fds_to_ignore(fds_to_close);
 
@@ -2645,6 +2655,7 @@
                                                          jintArray managed_session_socket_fds,
                                                          jboolean args_known,
                                                          jboolean is_priority_fork) {
+  ATRACE_CALL();
   std::vector<int> session_socket_fds =
       ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds)
           .value_or(std::vector<int>());
@@ -2660,6 +2671,7 @@
                     bool args_known,
                     bool is_priority_fork,
                     bool purge) {
+  ATRACE_CALL();
 
   std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
                    fds_to_ignore(fds_to_close);
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 104c023..10f75d0 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -612,6 +612,7 @@
     }
     optional Sounds sounds = 72;
 
+    optional SettingProto stylus_pointer_icon_enabled = 99 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
     // Defines whether managed profile ringtones should be synced from its
     // parent profile.
@@ -720,5 +721,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 99;
+    // Next tag = 100;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1e0cdd8..a425bb0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3222,6 +3222,13 @@
         android:description="@string/permdesc_accessHiddenProfile"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile
+        users.
+        @FlaggedApi("android.multiuser.flags.enable_permission_to_access_hidden_profiles") -->
+    <permission
+        android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
     <permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
                 android:protectionLevel="signature|role" />
@@ -7561,6 +7568,11 @@
     <permission android:name="android.permission.RESET_APP_ERRORS"
         android:protectionLevel="signature" />
 
+    <!-- @hide Allows ThemeOverlayController to delay launch of Home / SetupWizard on boot, ensuring
+         Theme Palettes and Colors are ready  -->
+    <permission android:name="android.permission.SET_THEME_OVERLAY_CONTROLLER_READY"
+        android:protectionLevel="signature|setup" />
+
     <!-- @hide Allows an application to create/destroy input consumer. -->
     <permission android:name="android.permission.INPUT_CONSUMER"
                 android:protectionLevel="signature" />
diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml
new file mode 100644
index 0000000..74dd6a0
--- /dev/null
+++ b/core/res/res/drawable/pointer_alias_vector.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#00FFFFFF"
+        android:pathData="M14.494 12.779a5.2 5.2 0 0 1-1.771 1.414 5.2 5.2 0 0 1-1.68.489l.81 1.658a1.968 1.968 0 0 0 3.536-1.728zM12.03 8.291l-.81-1.658a1.968 1.968 0 0 0-3.536 1.728l.896 1.833a5.2 5.2 0 0 1 1.77-1.414 5.2 5.2 0 0 1 1.68-.489" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="m18.323 13.178-.975-1.995a5.2 5.2 0 0 0-1.704-1.978 5.2 5.2 0 0 0-.517-2.01L14.152 5.2a5.232 5.232 0 0 0-9.401 4.594l.975 1.995a5.2 5.2 0 0 0 1.704 1.978 5.2 5.2 0 0 0 .517 2.01l.975 1.995a5.233 5.233 0 0 0 9.401-4.594m-2.843 6.1a4.23 4.23 0 0 1-5.66-1.944l-.975-1.995a4.2 4.2 0 0 1-.431-1.838l-.001-.276-.234-.146a4.2 4.2 0 0 1-1.555-1.729L5.65 9.355a4.232 4.232 0 1 1 7.604-3.716l.975 1.995c.29.594.428 1.22.431 1.838l.001.276.234.146c.648.405 1.194.99 1.555 1.728l.975 1.995a4.234 4.234 0 0 1-1.945 5.661" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M16.449 11.622a4.2 4.2 0 0 0-1.555-1.728l-.234-.146-.001-.276a4.2 4.2 0 0 0-.431-1.838l-.975-1.995a4.232 4.232 0 1 0-7.604 3.716l.975 1.995a4.2 4.2 0 0 0 1.555 1.729l.234.146.001.276c.002.617.141 1.244.431 1.838l.975 1.995a4.232 4.232 0 1 0 7.604-3.716zm-7.814.34-.459-.534a3 3 0 0 1-.416-.633L6.785 8.8a2.967 2.967 0 1 1 5.332-2.606l.975 1.995q.098.202.164.409l.214.672-.704-.02a4.2 4.2 0 0 0-1.977.429 4.2 4.2 0 0 0-1.801 1.675zm1.689-.33a2.97 2.97 0 0 1 1.975-1.1l1.205-.142-.755.95a2.95 2.95 0 0 1-1.02.818 3 3 0 0 1-.955.281l-1.204.143zm4.601 6.51a2.967 2.967 0 0 1-3.969-1.363l-.975-1.995a3 3 0 0 1-.164-.409l-.213-.671.704.02a4.2 4.2 0 0 0 1.977-.429 4.2 4.2 0 0 0 1.801-1.675l.353-.609.459.534q.245.284.416.633l.975 1.995a2.97 2.97 0 0 1-1.364 3.969" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_alias_vector_icon.xml b/core/res/res/drawable/pointer_alias_vector_icon.xml
new file mode 100644
index 0000000..6057a2e
--- /dev/null
+++ b/core/res/res/drawable/pointer_alias_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_alias_vector"
+    android:hotSpotX="11.5dp"
+    android:hotSpotY="11.5dp" />
diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml
new file mode 100644
index 0000000..1692e5e
--- /dev/null
+++ b/core/res/res/drawable/pointer_all_scroll_vector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M12.93 4.54a1.06 1.06 0 0 0-1.85 0L9.32 7.6c-.4.71.1 1.6.92 1.6h.82v1.86H9.2v-.84c0-.82-.88-1.33-1.6-.93l-3.06 1.76c-.7.41-.7 1.44 0 1.85l3.07 1.76c.7.4 1.6-.1 1.6-.93v-.79h1.86v1.87h-.82c-.81 0-1.33.88-.92 1.6l1.76 3.06c.4.71 1.44.71 1.85 0l1.75-3.07c.41-.7-.1-1.6-.92-1.6h-.82v-1.86h1.86v.8c0 .81.89 1.32 1.6.92l3.07-1.76c.7-.41.7-1.44 0-1.85L16.4 9.3c-.71-.4-1.6.1-1.6.93v.84h-1.86V9.2h.82c.82 0 1.33-.89.92-1.6l-1.75-3.06z"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_all_scroll_vector_icon.xml b/core/res/res/drawable/pointer_all_scroll_vector_icon.xml
new file mode 100644
index 0000000..d64b99a
--- /dev/null
+++ b/core/res/res/drawable/pointer_all_scroll_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_all_scroll_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml
new file mode 100644
index 0000000..562f0c0
--- /dev/null
+++ b/core/res/res/drawable/pointer_arrow_vector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M16.34 11.18 6.77 4.02a1.78 1.78 0 0 0-1.88-.17c-.63.31-1 .91-1 1.6l.01 11.96c0 .9.6 1.46 1.15 1.67a1.74 1.74 0 0 0 1.98-.45l2.96-3.19c.3-.32.7-.52 1.13-.56l4.33-.47a1.8 1.8 0 0 0 .89-3.23z"
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_arrow_vector_icon.xml b/core/res/res/drawable/pointer_arrow_vector_icon.xml
new file mode 100644
index 0000000..b7a8992
--- /dev/null
+++ b/core/res/res/drawable/pointer_arrow_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_arrow_vector"
+    android:hotSpotX="4.5dp"
+    android:hotSpotY="3.5dp" />
diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml
new file mode 100644
index 0000000..044a4f4
--- /dev/null
+++ b/core/res/res/drawable/pointer_cell_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M19 10.667h-5.668V5a1 1 0 0 0-1-1h-.667a1 1 0 0 0-1 1v5.667H5a1 1 0 0 0-1 1v.667a1 1 0 0 0 1 1h5.665V19a1 1 0 0 0 1 1h.667a1 1 0 0 0 1-1v-5.666H19a1 1 0 0 0 1-1v-.667a1 1 0 0 0-1-1" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_cell_vector_icon.xml b/core/res/res/drawable/pointer_cell_vector_icon.xml
new file mode 100644
index 0000000..9e0f632
--- /dev/null
+++ b/core/res/res/drawable/pointer_cell_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_cell_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml
new file mode 100644
index 0000000..8e954d2
--- /dev/null
+++ b/core/res/res/drawable/pointer_context_menu_vector.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" />
+        <path android:fillColor="#FFFFFF" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" />
+    </group>
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M19.475 3.461h-2.66c-.37 0-.67.3-.67.67v2.66c0 .37.3.67.67.67h2.66c.37 0 .67-.3.67-.67v-2.66a.67.67 0 0 0-.67-.67m-.3 3.062h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.868h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.885h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_context_menu_vector_icon.xml b/core/res/res/drawable/pointer_context_menu_vector_icon.xml
new file mode 100644
index 0000000..90f9043
--- /dev/null
+++ b/core/res/res/drawable/pointer_context_menu_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_context_menu_vector"
+    android:hotSpotX="4.5dp"
+    android:hotSpotY="3.5dp" />
diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml
new file mode 100644
index 0000000..b1e8995
--- /dev/null
+++ b/core/res/res/drawable/pointer_copy_vector.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" />
+        <path android:fillColor="#FFFFFF" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" />
+    </group>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18.485 10.884v1.739q.013.189.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.062 0 .123.007.185.009A4.4 4.4 0 0 1 13 6.5c0-.378.061-.739.149-1.09a1.97 1.97 0 0 0-1.659-.926c-.903 0-1.658.603-1.906 1.425a1.997 1.997 0 0 0-3.091 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.108c-.316.156-.645.29-.999.37" />
+    <path
+        android:fillColor="#1FA54A"
+        android:pathData="M17.5 3C15.57 3 14 4.57 14 6.5s1.57 3.5 3.5 3.5S21 8.43 21 6.5 19.43 3 17.5 3m1.799 4H18v1.3a.5.5 0 0 1-1 0V7h-1.301a.5.5 0 0 1 0-1H17V4.7a.5.5 0 0 1 1 0V6h1.299a.5.5 0 0 1 0 1" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_copy_vector_icon.xml b/core/res/res/drawable/pointer_copy_vector_icon.xml
new file mode 100644
index 0000000..fe2db15
--- /dev/null
+++ b/core/res/res/drawable/pointer_copy_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_copy_vector"
+    android:hotSpotX="8.5dp"
+    android:hotSpotY="7.5dp" />
diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml
new file mode 100644
index 0000000..b2e7e8a
--- /dev/null
+++ b/core/res/res/drawable/pointer_crosshair_vector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" />
+    <path
+        android:fillType="evenOdd"
+        android:fillColor="#000000"
+        android:pathData="M19.25 11.25h-6.5v-6.5a.75.75 0 0 0-1.5 0v6.5h-6.5a.75.75 0 0 0 0 1.5h6.5v6.5a.75.75 0 0 0 1.5 0v-6.5h6.5a.75.75 0 0 0 0-1.5" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_crosshair_vector_icon.xml b/core/res/res/drawable/pointer_crosshair_vector_icon.xml
new file mode 100644
index 0000000..d938514
--- /dev/null
+++ b/core/res/res/drawable/pointer_crosshair_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_crosshair_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml
new file mode 100644
index 0000000..7d9f048
--- /dev/null
+++ b/core/res/res/drawable/pointer_grab_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19.442 14.581V7.562a1 1 0 0 0-2 0v2.997a5.7 5.7 0 0 0-1-.663v-5.21a1 1 0 0 0-2 0v4.615a5.5 5.5 0 0 0-1-.011V3a1 1 0 0 0-2 0v6.835a5.6 5.6 0 0 0-.996.623V4.625a1 1 0 0 0-2 0v8.955l-1.962-2.008a1.215 1.215 0 0 0-1.747 0 1.286 1.286 0 0 0 0 1.788l5.106 5.224q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.046 0 5.565-2.469 5.565-5.516z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_grab_vector_icon.xml b/core/res/res/drawable/pointer_grab_vector_icon.xml
new file mode 100644
index 0000000..6ff7082
--- /dev/null
+++ b/core/res/res/drawable/pointer_grab_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_grab_vector"
+    android:hotSpotX="9.5dp"
+    android:hotSpotY="4.5dp" />
diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml
new file mode 100644
index 0000000..9c96103
--- /dev/null
+++ b/core/res/res/drawable/pointer_grabbing_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M18.485 12.622V8.508a1 1 0 0 0-2 0v.259a5.6 5.6 0 0 0-1-.66v-.599a1 1 0 0 0-2 0v.008a5.6 5.6 0 0 0-.996-.009V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514a5 5 0 0 0-.012-.381" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_grabbing_vector_icon.xml b/core/res/res/drawable/pointer_grabbing_vector_icon.xml
new file mode 100644
index 0000000..903c693
--- /dev/null
+++ b/core/res/res/drawable/pointer_grabbing_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_grabbing_vector"
+    android:hotSpotX="8.5dp"
+    android:hotSpotY="7.5dp" />
diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml
new file mode 100644
index 0000000..79792f8
--- /dev/null
+++ b/core/res/res/drawable/pointer_hand_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19.496 10.999A.997.997 0 0 0 18.5 10a.995.995 0 0 0-.992.98v.644a.5.5 0 0 1-.498.5c-.014 0-.026-.007-.04-.008a.493.493 0 0 1-.457-.491v-1.02l-.004-.003V10c0-.552-.446-.999-.996-.999s-.996.447-.996.999v.008h-.001l-.005 1.003-.001.154a.5.5 0 0 1-.498.497h-.003a.5.5 0 0 1-.495-.502l.001-.159.006-1.227v-.796a.997.997 0 0 0-.996-.999.993.993 0 0 0-.988.96v2.185a.496.496 0 0 1-.989.037l-.011.007v-7.17a.997.997 0 0 0-.996-.999.997.997 0 0 0-.996.999V14.28l-1.95-2.002a1.21 1.21 0 0 0-1.74 0 1.286 1.286 0 0 0 0 1.786l5.084 5.219q.056.057.117.106A5.54 5.54 0 0 0 13.962 21c3.033 0 5.541-2.467 5.541-5.51a6 6 0 0 0-.008-.293z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_hand_vector_icon.xml b/core/res/res/drawable/pointer_hand_vector_icon.xml
new file mode 100644
index 0000000..c59c8dc
--- /dev/null
+++ b/core/res/res/drawable/pointer_hand_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_hand_vector"
+    android:hotSpotX="9.5dp"
+    android:hotSpotY="2.5dp" />
diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml
new file mode 100644
index 0000000..09f3e31
--- /dev/null
+++ b/core/res/res/drawable/pointer_handwriting_vector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" />
+        <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" />
+    </group>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M5 4c-.55 0-1 .45-1 1v14c0 .55.45 1 1 1s1-.45 1-1V5c0-.55-.45-1-1-1m14.41 3.51-1.42-1.42c-.39-.39-.9-.59-1.41-.59h-.01c-.51 0-1.02.2-1.41.59L8 13.25v4.25h4.25l7.16-7.16c.78-.78.78-2.05 0-2.83m-7.839 7.85-1.43-1.43 6.29-6.29h.01l1.42 1.43z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_handwriting_vector_icon.xml b/core/res/res/drawable/pointer_handwriting_vector_icon.xml
new file mode 100644
index 0000000..14a8700
--- /dev/null
+++ b/core/res/res/drawable/pointer_handwriting_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_handwriting_vector"
+    android:hotSpotX="8.25dp"
+    android:hotSpotY="23.75dp" />
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml
new file mode 100644
index 0000000..6b7fd9f
--- /dev/null
+++ b/core/res/res/drawable/pointer_help_vector.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M17.73 7.254a.55.55 0 0 0-.407.169.55.55 0 0 0-.169.407q0 .225.169.401a.55.55 0 0 0 .808 0 .56.56 0 0 0 .175-.413.53.53 0 0 0-.175-.394.56.56 0 0 0-.401-.17m1.202-4.288q-.413-.42-1.126-.419-.651 0-1.164.357a2.1 2.1 0 0 0-.751.945l.864.326q.15-.363.407-.551a.93.93 0 0 1 .557-.188q.313 0 .526.182c.213.182.213.286.213.495q0 .226-.144.457a1.4 1.4 0 0 1-.394.432q-.35.3-.538.657c-.125.238-.187.485-.187.86l.926.06q0-.3.081-.57t.469-.595q.363-.276.519-.601t.156-.726q-.002-.701-.414-1.121" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_help_vector_icon.xml b/core/res/res/drawable/pointer_help_vector_icon.xml
new file mode 100644
index 0000000..78cc3e9
--- /dev/null
+++ b/core/res/res/drawable/pointer_help_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_help_vector"
+    android:hotSpotX="4.5dp"
+    android:hotSpotY="3.5dp" />
diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
new file mode 100644
index 0000000..d1aea9e
--- /dev/null
+++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M19.465 11.052 16.4 9.294a1.065 1.065 0 0 0-1.596.922v.843H9.195v-.843c0-.818-.886-1.33-1.596-.922l-3.065 1.758a1.063 1.063 0 0 0 0 1.845l3.065 1.758a1.065 1.065 0 0 0 1.596-.922v-.791h5.609v.791c0 .818.886 1.33 1.596.922l3.065-1.758a1.063 1.063 0 0 0 0-1.845" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml
new file mode 100644
index 0000000..cee5f919
--- /dev/null
+++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_horizontal_double_arrow_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml
new file mode 100644
index 0000000..3a38bab
--- /dev/null
+++ b/core/res/res/drawable/pointer_nodrop_vector.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" />
+        <path android:fillColor="#FFFFFF" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" />
+    </group>
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18.485 10.932v1.69q.013.188.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.046 0 .09.006.135.007a4.5 4.5 0 0 1-.117-.995c0-.399.068-.779.165-1.148a1.97 1.97 0 0 0-1.629-.867c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-1.091-.327 2 2 0 0 0-2 2v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.056a4.5 4.5 0 0 1-.999.366" />
+    <path
+        android:fillColor="#B22A25"
+        android:pathData="M17.5 3C15.57 3 14 4.57 14 6.5s1.57 3.5 3.5 3.5S21 8.43 21 6.5 19.43 3 17.5 3m0 6A2.5 2.5 0 0 1 15 6.5c0-.525.164-1.011.441-1.413l3.472 3.472A2.5 2.5 0 0 1 17.5 9m2.105-1.163-3.442-3.442A2.5 2.5 0 0 1 17.5 4C18.879 4 20 5.121 20 6.5c0 .493-.148.95-.395 1.337" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_nodrop_vector_icon.xml b/core/res/res/drawable/pointer_nodrop_vector_icon.xml
new file mode 100644
index 0000000..ceba002
--- /dev/null
+++ b/core/res/res/drawable/pointer_nodrop_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_nodrop_vector"
+    android:hotSpotX="8.5dp"
+    android:hotSpotY="7.5dp" />
diff --git a/core/res/res/drawable/pointer_spot_anchor_vector.xml b/core/res/res/drawable/pointer_spot_anchor_vector.xml
new file mode 100644
index 0000000..54de2ae
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_anchor_vector.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
+        <path android:fillColor="#ADC6E7" android:pathData="M12 5c-3.859 0-7 3.14-7 7s3.141 7 7 7 7-3.141 7-7-3.141-7-7-7m0 13c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml b/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml
new file mode 100644
index 0000000..83b767c
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_anchor_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_anchor_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_spot_hover_vector.xml b/core/res/res/drawable/pointer_spot_hover_vector.xml
new file mode 100644
index 0000000..ef596c4
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_hover_vector.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
+        <path android:fillColor="#ADC6E7" android:pathData="M12 7c-2.757 0-5 2.243-5 5s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5m0 9c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_hover_vector_icon.xml b/core/res/res/drawable/pointer_spot_hover_vector_icon.xml
new file mode 100644
index 0000000..f892958
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_hover_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_hover_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_spot_touch_vector.xml b/core/res/res/drawable/pointer_spot_touch_vector.xml
new file mode 100644
index 0000000..afd2956
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_touch_vector.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#ADC6E7"
+        android:pathData="M21 12c0-4.963-4.038-9-9-9s-9 4.037-9 9 4.038 9 9 9 9-4.037 9-9m-9 8c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_touch_vector_icon.xml b/core/res/res/drawable/pointer_spot_touch_vector_icon.xml
new file mode 100644
index 0000000..7b96938
--- /dev/null
+++ b/core/res/res/drawable/pointer_spot_touch_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_touch_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml
new file mode 100644
index 0000000..9e44f28
--- /dev/null
+++ b/core/res/res/drawable/pointer_text_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M12 2c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2s2-.897 2-2V4c0-1.103-.897-2-2-2m1 16a1.001 1.001 0 0 1-2 0V4a1.001 1.001 0 0 1 2 0z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_text_vector_icon.xml b/core/res/res/drawable/pointer_text_vector_icon.xml
new file mode 100644
index 0000000..b03f8da
--- /dev/null
+++ b/core/res/res/drawable/pointer_text_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_text_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="11dp" />
diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
new file mode 100644
index 0000000..e5d5301
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" />
+    <path
+        android:fillType="evenOdd"
+        android:fillColor="#000000"
+        android:pathData="M6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.476l.924 3.41a1.063 1.063 0 0 1-1.304 1.304l-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml
new file mode 100644
index 0000000..7fd2a7f
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_top_left_diagonal_double_arrow_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
new file mode 100644
index 0000000..e6f7aaf
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="m16.626 6.069-3.41.924a1.065 1.065 0 0 0-.476 1.781l.579.579-3.966 3.966-.579-.579a1.066 1.066 0 0 0-1.781.477l-.924 3.41a1.063 1.063 0 0 0 1.304 1.304l3.41-.924a1.065 1.065 0 0 0 .476-1.781l-.578-.578 3.966-3.966.577.577a1.066 1.066 0 0 0 1.781-.476l.924-3.41a1.062 1.062 0 0 0-1.303-1.304" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml
new file mode 100644
index 0000000..d2516b1
--- /dev/null
+++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_top_right_diagonal_double_arrow_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
new file mode 100644
index 0000000..6ffcfef
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M13.761 14.805h-.817v-5.61h.817c.818 0 1.33-.886.922-1.596l-1.758-3.065a1.063 1.063 0 0 0-1.845 0L9.323 7.599c-.407.71.104 1.596.922 1.596h.818v5.609h-.818c-.818 0-1.33.886-.922 1.596l1.758 3.065a1.063 1.063 0 0 0 1.845 0l1.758-3.065a1.065 1.065 0 0 0-.923-1.595" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml
new file mode 100644
index 0000000..64f3424
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_vertical_double_arrow_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml
new file mode 100644
index 0000000..72f40cc
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_text_vector.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#000000"
+        android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" />
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19 10H5c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2m0 3H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_vertical_text_vector_icon.xml b/core/res/res/drawable/pointer_vertical_text_vector_icon.xml
new file mode 100644
index 0000000..afb225a
--- /dev/null
+++ b/core/res/res/drawable/pointer_vertical_text_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_vertical_text_vector"
+    android:hotSpotX="12dp"
+    android:hotSpotY="12dp" />
diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml
new file mode 100644
index 0000000..8921666
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_in_vector.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
+        <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" />
+    </group>
+    <group>
+        <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
+        <path android:fillColor="#000000" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_zoom_in_vector_icon.xml b/core/res/res/drawable/pointer_zoom_in_vector_icon.xml
new file mode 100644
index 0000000..fcc0c28
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_in_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_zoom_in_vector"
+    android:hotSpotX="10.5dp"
+    android:hotSpotY="9.5dp" />
diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml
new file mode 100644
index 0000000..815ce0e
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_out_vector.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <group>
+        <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
+        <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" />
+    </group>
+    <group>
+        <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
+        <path android:fillColor="#000000" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" />
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_zoom_out_vector_icon.xml b/core/res/res/drawable/pointer_zoom_out_vector_icon.xml
new file mode 100644
index 0000000..37f4e79
--- /dev/null
+++ b/core/res/res/drawable/pointer_zoom_out_vector_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_zoom_out_vector"
+    android:hotSpotX="10.5dp"
+    android:hotSpotY="9.5dp" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d910940..4741012 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3278,6 +3278,31 @@
              <p> By default, the behavior is configured by the same attribute in application.
         -->
         <attr name="enableOnBackInvokedCallback" format="boolean"/>
+
+        <!-- Specifies permissions necessary to launch this activity via
+             {@link android.content.Context#startActivity} when passing content URIs. The default
+             value is {@code none}, meaning no specific permissions are required. Setting this
+             attribute restricts activity invocation based on the invoker's permissions. If the
+             invoker doesn't have the required permissions, the activity start will be denied via a
+             {@link java.lang.SecurityException}.
+
+             <p> Note that the enforcement works for content URIs inside
+             {@link android.content.Intent#getData} and {@link android.content.Intent#getClipData}.
+             @FlaggedApi("android.security.content_uri_permission_apis") -->
+        <attr name="requireContentUriPermissionFromCaller" format="string">
+            <!-- Default, no specific permissions are required. -->
+            <enum name="none" value="0" />
+            <!-- Enforces the invoker to have read access to the passed content URIs. -->
+            <enum name="read" value="1" />
+            <!-- Enforces the invoker to have write access to the passed content URIs. -->
+            <enum name="write" value="2" />
+            <!-- Enforces the invoker to have either read or write access to the passed content
+                 URIs. -->
+            <enum name="readOrWrite" value="3" />
+            <!-- Enforces the invoker to have both read and write access to the passed content
+                 URIs. -->
+            <enum name="readAndWrite" value="4" />
+        </attr>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 4799c37..f9cf28c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -149,6 +149,8 @@
     <public name="autoTransact"/>
     <!-- @FlaggedApi("com.android.window.flags.enforce_edge_to_edge") -->
     <public name="windowOptOutEdgeToEdgeEnforcement"/>
+    <!-- @FlaggedApi("android.security.content_uri_permission_apis") -->
+    <public name="requireContentUriPermissionFromCaller" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01bc0000">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 22d028c..adf8d9f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1460,6 +1460,43 @@
     </style>
 
     <!-- @hide -->
+    <style name="VectorPointer">
+        <item name="pointerIconArrow">@drawable/pointer_arrow_vector_icon</item>
+        <item name="pointerIconSpotHover">@drawable/pointer_spot_hover_vector_icon</item>
+        <item name="pointerIconSpotTouch">@drawable/pointer_spot_touch_vector_icon</item>
+        <item name="pointerIconSpotAnchor">@drawable/pointer_spot_anchor_vector_icon</item>
+        <item name="pointerIconHand">@drawable/pointer_hand_vector_icon</item>
+        <item name="pointerIconContextMenu">@drawable/pointer_context_menu_vector_icon</item>
+        <item name="pointerIconHelp">@drawable/pointer_help_vector_icon</item>
+        <item name="pointerIconWait">@drawable/pointer_wait_icon</item>
+        <item name="pointerIconCell">@drawable/pointer_cell_vector_icon</item>
+        <item name="pointerIconCrosshair">@drawable/pointer_crosshair_vector_icon</item>
+        <item name="pointerIconText">@drawable/pointer_text_vector_icon</item>
+        <item name="pointerIconVerticalText">@drawable/pointer_vertical_text_vector_icon</item>
+        <item name="pointerIconAlias">@drawable/pointer_alias_vector_icon</item>
+        <item name="pointerIconCopy">@drawable/pointer_copy_vector_icon</item>
+        <item name="pointerIconAllScroll">@drawable/pointer_all_scroll_vector_icon</item>
+        <item name="pointerIconNodrop">@drawable/pointer_nodrop_vector_icon</item>
+        <item name="pointerIconHorizontalDoubleArrow">
+            @drawable/pointer_horizontal_double_arrow_vector_icon
+        </item>
+        <item name="pointerIconVerticalDoubleArrow">
+            @drawable/pointer_vertical_double_arrow_vector_icon
+        </item>
+        <item name="pointerIconTopRightDiagonalDoubleArrow">
+            @drawable/pointer_top_right_diagonal_double_arrow_vector_icon
+        </item>
+        <item name="pointerIconTopLeftDiagonalDoubleArrow">
+            @drawable/pointer_top_left_diagonal_double_arrow_vector_icon
+        </item>
+        <item name="pointerIconZoomIn">@drawable/pointer_zoom_in_vector_icon</item>
+        <item name="pointerIconZoomOut">@drawable/pointer_zoom_out_vector_icon</item>
+        <item name="pointerIconGrab">@drawable/pointer_grab_vector_icon</item>
+        <item name="pointerIconGrabbing">@drawable/pointer_grabbing_vector_icon</item>
+        <item name="pointerIconHandwriting">@drawable/pointer_handwriting_vector_icon</item>
+    </style>
+
+    <!-- @hide -->
     <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">
         <item name="minHeight">?attr/listPreferredItemHeightSmall</item>
         <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 58427b1..3df7570 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1681,6 +1681,7 @@
   <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" />
   <java-symbol type="style" name="Pointer" />
   <java-symbol type="style" name="LargePointer" />
+  <java-symbol type="style" name="VectorPointer" />
   <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" />
   <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" />
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 13d38d2..9d1e507 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -124,6 +124,10 @@
         <group gid="security_log_writer" />
     </permission>
 
+    <permission name="android.permission.MANAGE_VIRTUAL_MACHINE">
+        <group gid="virtualmachine" />
+    </permission>
+
     <!-- These are permissions that were mapped to gids but we need
          to keep them here until an upgrade from L to the current
          version is to be supported. These permissions are built-in
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index bf60944..7823277 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -108,6 +108,7 @@
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
         <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
     </install-in-user-type>
 
     <!--  Settings (Settings app) -->
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 9854e58..a80afe2 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -45,9 +45,6 @@
     <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
     <bool name="config_pipEnableResizeForMenu">true</bool>
 
-    <!-- Allow PIP to resize via dragging the corner of PiP. -->
-    <bool name="config_pipEnableDragCornerResize">false</bool>
-
     <!-- PiP minimum size, which is a % based off the shorter side of display width and height -->
     <fraction name="config_pipShortestEdgePercent">40%</fraction>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
index 160f922..55982dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
@@ -310,12 +310,16 @@
         float top = mapRange(progress, mEnteringStartRect.top, mStartTaskRect.top);
         float width = mapRange(progress, mEnteringStartRect.width(), mStartTaskRect.width());
         float height = mapRange(progress, mEnteringStartRect.height(), mStartTaskRect.height());
-        float alpha = mapRange(progress, mEnteringProgress, 1.0f);
-
+        float alpha = mapRange(progress, getPreCommitEnteringAlpha(), 1.0f);
         mEnteringRect.set(left, top, left + width, top + height);
         applyTransform(mEnteringTarget.leash, mEnteringRect, alpha);
     }
 
+    private float getPreCommitEnteringAlpha() {
+        return Math.max(smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
+                MIN_WINDOW_ALPHA);
+    }
+
     private float getEnteringProgress() {
         return mEnteringProgress * SCALE_FACTOR;
     }
@@ -325,9 +329,7 @@
         if (mEnteringTarget != null && mEnteringTarget.leash != null) {
             transformWithProgress(
                     mEnteringProgress,
-                    Math.max(
-                            smoothstep(ENTER_ALPHA_THRESHOLD, 0.7f, mEnteringProgress),
-                            MIN_WINDOW_ALPHA),  /* alpha */
+                    getPreCommitEnteringAlpha(),
                     mEnteringTarget.leash,
                     mEnteringRect,
                     -mWindowXShift,
@@ -336,6 +338,11 @@
         }
     }
 
+    private float getPreCommitLeavingAlpha() {
+        return Math.max(1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress),
+                MIN_WINDOW_ALPHA);
+    }
+
     private float getLeavingProgress() {
         return mLeavingProgress * SCALE_FACTOR;
     }
@@ -345,9 +352,7 @@
         if (mClosingTarget != null && mClosingTarget.leash != null) {
             transformWithProgress(
                     mLeavingProgress,
-                    Math.max(
-                            1 - smoothstep(0, ENTER_ALPHA_THRESHOLD, mLeavingProgress),
-                            MIN_WINDOW_ALPHA),
+                    getPreCommitLeavingAlpha(),
                     mClosingTarget.leash,
                     mClosingRect,
                     0,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index a2a2914..da530d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -53,8 +53,6 @@
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.common.bubbles.BubbleInfo;
-import com.android.wm.shell.taskview.TaskView;
-import com.android.wm.shell.taskview.TaskViewTaskController;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -403,13 +401,9 @@
      * Returns the existing {@link #mBubbleTaskView} if it's not {@code null}. Otherwise a new
      * instance of {@link BubbleTaskView} is created.
      */
-    public BubbleTaskView getOrCreateBubbleTaskView(Context context, BubbleController controller) {
+    public BubbleTaskView getOrCreateBubbleTaskView(BubbleTaskViewFactory taskViewFactory) {
         if (mBubbleTaskView == null) {
-            TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context,
-                    controller.getTaskOrganizer(),
-                    controller.getTaskViewTransitions(), controller.getSyncTransactionQueue());
-            TaskView taskView = new TaskView(context, taskViewTaskController);
-            mBubbleTaskView = new BubbleTaskView(taskView, controller.getMainExecutor());
+            mBubbleTaskView = taskViewFactory.create();
         }
         return mBubbleTaskView;
     }
@@ -514,14 +508,18 @@
      *
      * @param callback the callback to notify one the bubble is ready to be displayed.
      * @param context the context for the bubble.
-     * @param controller the bubble controller.
+     * @param expandedViewManager the bubble expanded view manager.
+     * @param taskViewFactory the task view factory used to create the task view for the bubble.
+     * @param positioner the bubble positioner.
      * @param stackView the view the bubble is added to, iff showing as floating.
      * @param layerView the layer the bubble is added to, iff showing in the bubble bar.
-     * @param iconFactory the icon factory use to create images for the bubble.
+     * @param iconFactory the icon factory used to create images for the bubble.
      */
     void inflate(BubbleViewInfoTask.Callback callback,
             Context context,
-            BubbleController controller,
+            BubbleExpandedViewManager expandedViewManager,
+            BubbleTaskViewFactory taskViewFactory,
+            BubblePositioner positioner,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory iconFactory,
@@ -531,7 +529,9 @@
         }
         mInflationTask = new BubbleViewInfoTask(this,
                 context,
-                controller,
+                expandedViewManager,
+                taskViewFactory,
+                positioner,
                 stackView,
                 layerView,
                 iconFactory,
@@ -912,9 +912,8 @@
         if (uid != -1) {
             intent.putExtra(Settings.EXTRA_APP_UID, uid);
         }
-        intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         return intent;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 0aa8959..5c6f73f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -113,6 +113,7 @@
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.taskview.TaskView;
+import com.android.wm.shell.taskview.TaskViewTaskController;
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.Transitions;
 
@@ -190,6 +191,8 @@
     private final ShellCommandHandler mShellCommandHandler;
     private final IWindowManager mWmService;
     private final BubbleProperties mBubbleProperties;
+    private final BubbleTaskViewFactory mBubbleTaskViewFactory;
+    private final BubbleExpandedViewManager mExpandedViewManager;
 
     // Used to post to main UI thread
     private final ShellExecutor mMainExecutor;
@@ -333,6 +336,16 @@
         mWmService = wmService;
         mBubbleProperties = bubbleProperties;
         shellInit.addInitCallback(this::onInit, this);
+        mBubbleTaskViewFactory = new BubbleTaskViewFactory() {
+            @Override
+            public BubbleTaskView create() {
+                TaskViewTaskController taskViewTaskController = new TaskViewTaskController(
+                        context, organizer, taskViewTransitions, syncQueue);
+                TaskView taskView = new TaskView(context, taskViewTaskController);
+                return new BubbleTaskView(taskView, mainExecutor);
+            }
+        };
+        mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(this);
     }
 
     private void registerOneHandedState(OneHandedController oneHanded) {
@@ -802,7 +815,13 @@
         try {
             mAddedToWindowManager = true;
             registerBroadcastReceiver();
-            mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar());
+            if (isShowingAsBubbleBar()) {
+                mBubbleData.getOverflow().initializeForBubbleBar(
+                        mExpandedViewManager, mBubblePositioner);
+            } else {
+                mBubbleData.getOverflow().initialize(
+                        mExpandedViewManager, mStackView, mBubblePositioner);
+            }
             // (TODO: b/273314541) some duplication in the inset listener
             if (isShowingAsBubbleBar()) {
                 mWindowManager.addView(mLayerView, mWmLayoutParams);
@@ -984,7 +1003,9 @@
         for (Bubble b : mBubbleData.getBubbles()) {
             b.inflate(null /* callback */,
                     mContext,
-                    this,
+                    mExpandedViewManager,
+                    mBubbleTaskViewFactory,
+                    mBubblePositioner,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -993,7 +1014,9 @@
         for (Bubble b : mBubbleData.getOverflowBubbles()) {
             b.inflate(null /* callback */,
                     mContext,
-                    this,
+                    mExpandedViewManager,
+                    mBubbleTaskViewFactory,
+                    mBubblePositioner,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1377,7 +1400,9 @@
                 bubble.inflate(
                         (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
                         mContext,
-                        this,
+                        mExpandedViewManager,
+                        mBubbleTaskViewFactory,
+                        mBubblePositioner,
                         mStackView,
                         mLayerView,
                         mBubbleIconFactory,
@@ -1431,7 +1456,9 @@
             Bubble bubble = mBubbleData.getBubbles().get(i);
             bubble.inflate(callback,
                     mContext,
-                    this,
+                    mExpandedViewManager,
+                    mBubbleTaskViewFactory,
+                    mBubblePositioner,
                     mStackView,
                     mLayerView,
                     mBubbleIconFactory,
@@ -1506,8 +1533,14 @@
         // Lazy init stack view when a bubble is created
         ensureBubbleViewsAndWindowCreated();
         bubble.setInflateSynchronously(mInflateSynchronously);
-        bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
-                mContext, this, mStackView,  mLayerView,
+        bubble.inflate(
+                b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
+                mContext,
+                mExpandedViewManager,
+                mBubbleTaskViewFactory,
+                mBubblePositioner,
+                mStackView,
+                mLayerView,
                 mBubbleIconFactory,
                 false /* skipInflation */);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 6d3f0c3..6c2f925 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -37,10 +37,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubbles.DismissReason;
-import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.common.bubbles.RemovedBubble;
 
@@ -180,7 +178,7 @@
      * This interface reports changes to the state and appearance of bubbles which should be applied
      * as necessary to the UI.
      */
-    interface Listener {
+    public interface Listener {
         /** Reports changes have have occurred as a result of the most recent operation. */
         void applyUpdate(Update update);
     }
@@ -419,8 +417,10 @@
 
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
-     * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleController, BubbleStackView,
-     * BubbleBarLayerView, BubbleIconFactory, boolean)
+     * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
+     * BubbleTaskViewFactory, BubblePositioner, BubbleStackView,
+     * com.android.wm.shell.bubbles.bar.BubbleBarLayerView,
+     * com.android.launcher3.icons.BubbleIconFactory, boolean)
      */
     void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
         mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
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 088660e..df9ba63 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
@@ -184,7 +184,7 @@
     private boolean mIsOverflow;
     private boolean mIsClipping;
 
-    private BubbleController mController;
+    private BubbleExpandedViewManager mManager;
     private BubbleStackView mStackView;
     private BubblePositioner mPositioner;
 
@@ -261,7 +261,7 @@
                     // the bubble again so we'll just remove it.
                     Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
                             + ", " + e.getMessage() + "; removing bubble");
-                    mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
+                    mManager.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
                 }
             });
             mInitialized = true;
@@ -281,7 +281,7 @@
 
             if (mBubble != null && mBubble.isAppBubble()) {
                 // Let the controller know sooner what the taskId is.
-                mController.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
+                mManager.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
             }
 
             // With the task org, the taskAppeared callback will only happen once the task has
@@ -301,7 +301,7 @@
             ProtoLog.d(WM_SHELL_BUBBLES, "onTaskRemovalStarted: taskId=%d bubble=%s",
                     taskId, getBubbleKey());
             if (mBubble != null) {
-                mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
+                mManager.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
             }
             if (mTaskView != null) {
                 // Release the surface
@@ -421,17 +421,20 @@
      * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need
      * to be called after view inflate.
      */
-    void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow,
+    void initialize(BubbleExpandedViewManager expandedViewManager,
+            BubbleStackView stackView,
+            BubblePositioner positioner,
+            boolean isOverflow,
             @Nullable BubbleTaskView bubbleTaskView) {
-        mController = controller;
+        mManager = expandedViewManager;
         mStackView = stackView;
         mIsOverflow = isOverflow;
-        mPositioner = mController.getPositioner();
+        mPositioner = positioner;
 
         if (mIsOverflow) {
             mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
                     R.layout.bubble_overflow_container, null /* root */);
-            mOverflowView.setBubbleController(mController);
+            mOverflowView.initialize(expandedViewManager, positioner);
             FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
             mExpandedViewContainer.addView(mOverflowView, lp);
             mExpandedViewContainer.setLayoutParams(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
new file mode 100644
index 0000000..b0d3cc4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+/** Manager interface for bubble expanded views. */
+interface BubbleExpandedViewManager {
+
+    val overflowBubbles: List<Bubble>
+    fun setOverflowListener(listener: BubbleData.Listener)
+    fun collapseStack()
+    fun updateWindowFlagsForBackpress(intercept: Boolean)
+    fun promoteBubbleFromOverflow(bubble: Bubble)
+    fun removeBubble(key: String, reason: Int)
+    fun dismissBubble(bubble: Bubble, reason: Int)
+    fun setAppBubbleTaskId(key: String, taskId: Int)
+    fun isStackExpanded(): Boolean
+    fun isShowingAsBubbleBar(): Boolean
+
+    companion object {
+        /**
+         * Convenience function for creating a [BubbleExpandedViewManager] that delegates to the
+         * given `controller`.
+         */
+        @JvmStatic
+        fun fromBubbleController(controller: BubbleController): BubbleExpandedViewManager {
+            return object : BubbleExpandedViewManager {
+
+                override val overflowBubbles: List<Bubble>
+                    get() = controller.overflowBubbles
+
+                override fun setOverflowListener(listener: BubbleData.Listener) {
+                    controller.setOverflowListener(listener)
+                }
+
+                override fun collapseStack() {
+                    controller.collapseStack()
+                }
+
+                override fun updateWindowFlagsForBackpress(intercept: Boolean) {
+                    controller.updateWindowFlagsForBackpress(intercept)
+                }
+
+                override fun promoteBubbleFromOverflow(bubble: Bubble) {
+                    controller.promoteBubbleFromOverflow(bubble)
+                }
+
+                override fun removeBubble(key: String, reason: Int) {
+                    controller.removeBubble(key, reason)
+                }
+
+                override fun dismissBubble(bubble: Bubble, reason: Int) {
+                    controller.dismissBubble(bubble, reason)
+                }
+
+                override fun setAppBubbleTaskId(key: String, taskId: Int) {
+                    controller.setAppBubbleTaskId(key, taskId)
+                }
+
+                override fun isStackExpanded(): Boolean = controller.isStackExpanded
+
+                override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index e5d9ace..f32974e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -56,19 +56,32 @@
     }
 
     /** Call before use and again if cleanUpExpandedState was called. */
-    fun initialize(controller: BubbleController, forBubbleBar: Boolean) {
-        if (forBubbleBar) {
-            createBubbleBarExpandedView()
-                .initialize(controller, /* isOverflow= */ true, /* bubbleTaskView= */ null)
-        } else {
-            createExpandedView()
+    fun initialize(
+        expandedViewManager: BubbleExpandedViewManager,
+        stackView: BubbleStackView,
+        positioner: BubblePositioner
+    ) {
+        createExpandedView()
                 .initialize(
-                    controller,
-                    controller.stackView,
+                        expandedViewManager,
+                    stackView,
+                    positioner,
                     /* isOverflow= */ true,
                     /* bubbleTaskView= */ null
                 )
-        }
+    }
+
+    fun initializeForBubbleBar(
+        expandedViewManager: BubbleExpandedViewManager,
+        positioner: BubblePositioner
+    ) {
+        createBubbleBarExpandedView()
+            .initialize(
+                expandedViewManager,
+                positioner,
+                /* isOverflow= */ true,
+                /* bubbleTaskView= */ null
+            )
     }
 
     fun cleanUpExpandedState() {
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 70cdc82..b06de4f 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
@@ -62,7 +62,8 @@
     private ImageView mEmptyStateImage;
     private int mHorizontalMargin;
     private int mVerticalMargin;
-    private BubbleController mController;
+    private BubbleExpandedViewManager mExpandedViewManager;
+    private BubblePositioner mPositioner;
     private BubbleOverflowAdapter mAdapter;
     private RecyclerView mRecyclerView;
     private List<Bubble> mOverflowBubbles = new ArrayList<>();
@@ -70,7 +71,7 @@
     private View.OnKeyListener mKeyListener = (view, i, keyEvent) -> {
         if (keyEvent.getAction() == KeyEvent.ACTION_UP
                 && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-            mController.collapseStack();
+            mExpandedViewManager.collapseStack();
             return true;
         }
         return false;
@@ -126,8 +127,11 @@
         setFocusableInTouchMode(true);
     }
 
-    public void setBubbleController(BubbleController controller) {
-        mController = controller;
+    /** Initializes the view. Must be called after creation. */
+    public void initialize(BubbleExpandedViewManager expandedViewManager,
+            BubblePositioner positioner) {
+        mExpandedViewManager = expandedViewManager;
+        mPositioner = positioner;
     }
 
     public void show() {
@@ -149,9 +153,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        if (mController != null) {
+        if (mExpandedViewManager != null) {
             // For the overflow to get key events (e.g. back press) we need to adjust the flags
-            mController.updateWindowFlagsForBackpress(true);
+            mExpandedViewManager.updateWindowFlagsForBackpress(true);
         }
         setOnKeyListener(mKeyListener);
     }
@@ -159,8 +163,8 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mController != null) {
-            mController.updateWindowFlagsForBackpress(false);
+        if (mExpandedViewManager != null) {
+            mExpandedViewManager.updateWindowFlagsForBackpress(false);
         }
         setOnKeyListener(null);
     }
@@ -177,15 +181,15 @@
             mRecyclerView.addItemDecoration(new OverflowItemDecoration());
         }
         mAdapter = new BubbleOverflowAdapter(getContext(), mOverflowBubbles,
-                mController::promoteBubbleFromOverflow,
-                mController.getPositioner());
+                mExpandedViewManager::promoteBubbleFromOverflow,
+                mPositioner);
         mRecyclerView.setAdapter(mAdapter);
 
         mOverflowBubbles.clear();
-        mOverflowBubbles.addAll(mController.getOverflowBubbles());
+        mOverflowBubbles.addAll(mExpandedViewManager.getOverflowBubbles());
         mAdapter.notifyDataSetChanged();
 
-        mController.setOverflowListener(mDataListener);
+        mExpandedViewManager.setOverflowListener(mDataListener);
         updateEmptyStateVisibility();
         updateTheme();
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.kt
index 128f58b..230626f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewFactory.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.animation
+package com.android.wm.shell.bubbles
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+/** Factory for creating [BubbleTaskView]s. */
+fun interface BubbleTaskViewFactory {
+    /** Creates a new instance of [BubbleTaskView]. */
+    fun create(): BubbleTaskView
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 530ec5a..21b70b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -62,7 +62,7 @@
     }
 
     private final Context mContext;
-    private final BubbleController mController;
+    private final BubbleExpandedViewManager mExpandedViewManager;
     private final BubbleTaskViewHelper.Listener mListener;
     private final View mParentView;
 
@@ -142,7 +142,8 @@
                     // the bubble again so we'll just remove it.
                     Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
                             + ", " + e.getMessage() + "; removing bubble");
-                    mController.removeBubble(getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
+                    mExpandedViewManager.removeBubble(
+                            getBubbleKey(), Bubbles.DISMISS_INVALID_INTENT);
                 }
                 mInitialized = true;
             });
@@ -175,7 +176,7 @@
             ProtoLog.d(WM_SHELL_BUBBLES, "onTaskRemovalStarted: taskId=%d bubble=%s",
                     taskId, getBubbleKey());
             if (mBubble != null) {
-                mController.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
+                mExpandedViewManager.removeBubble(mBubble.getKey(), Bubbles.DISMISS_TASK_FINISHED);
             }
             if (mTaskView != null) {
                 mTaskView.release();
@@ -186,19 +187,19 @@
 
         @Override
         public void onBackPressedOnTaskRoot(int taskId) {
-            if (mTaskId == taskId && mController.isStackExpanded()) {
+            if (mTaskId == taskId && mExpandedViewManager.isStackExpanded()) {
                 mListener.onBackPressed();
             }
         }
     };
 
     public BubbleTaskViewHelper(Context context,
-            BubbleController controller,
+            BubbleExpandedViewManager expandedViewManager,
             BubbleTaskViewHelper.Listener listener,
             BubbleTaskView bubbleTaskView,
             View parent) {
         mContext = context;
-        mController = controller;
+        mExpandedViewManager = expandedViewManager;
         mListener = listener;
         mParentView = parent;
         mTaskView = bubbleTaskView.getTaskView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 5fc10a9..69119cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -70,7 +70,9 @@
 
     private Bubble mBubble;
     private WeakReference<Context> mContext;
-    private WeakReference<BubbleController> mController;
+    private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
+    private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
+    private WeakReference<BubblePositioner> mPositioner;
     private WeakReference<BubbleStackView> mStackView;
     private WeakReference<BubbleBarLayerView> mLayerView;
     private BubbleIconFactory mIconFactory;
@@ -84,7 +86,9 @@
      */
     BubbleViewInfoTask(Bubble b,
             Context context,
-            BubbleController controller,
+            BubbleExpandedViewManager expandedViewManager,
+            BubbleTaskViewFactory taskViewFactory,
+            BubblePositioner positioner,
             @Nullable BubbleStackView stackView,
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory factory,
@@ -93,7 +97,9 @@
             Executor mainExecutor) {
         mBubble = b;
         mContext = new WeakReference<>(context);
-        mController = new WeakReference<>(controller);
+        mExpandedViewManager = new WeakReference<>(expandedViewManager);
+        mTaskViewFactory = new WeakReference<>(taskViewFactory);
+        mPositioner = new WeakReference<>(positioner);
         mStackView = new WeakReference<>(stackView);
         mLayerView = new WeakReference<>(layerView);
         mIconFactory = factory;
@@ -109,11 +115,13 @@
             return null;
         }
         if (mLayerView.get() != null) {
-            return BubbleViewInfo.populateForBubbleBar(mContext.get(), mController.get(),
-                    mLayerView.get(), mIconFactory, mBubble, mSkipInflation);
+            return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
+                    mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
+                    mBubble, mSkipInflation);
         } else {
-            return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
-                    mIconFactory, mBubble, mSkipInflation);
+            return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
+                    mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
+                    mBubble, mSkipInflation);
         }
     }
 
@@ -135,7 +143,7 @@
     }
 
     private boolean verifyState() {
-        if (mController.get().isShowingAsBubbleBar()) {
+        if (mExpandedViewManager.get().isShowingAsBubbleBar()) {
             return mLayerView.get() != null;
         } else {
             return mStackView.get() != null;
@@ -167,18 +175,23 @@
         Bitmap badgeBitmap;
 
         @Nullable
-        public static BubbleViewInfo populateForBubbleBar(Context c, BubbleController controller,
-                BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b,
+        public static BubbleViewInfo populateForBubbleBar(Context c,
+                BubbleExpandedViewManager expandedViewManager,
+                BubbleTaskViewFactory taskViewFactory,
+                BubblePositioner positioner,
+                BubbleBarLayerView layerView,
+                BubbleIconFactory iconFactory,
+                Bubble b,
                 boolean skipInflation) {
             BubbleViewInfo info = new BubbleViewInfo();
 
             if (!skipInflation && !b.isInflated()) {
-                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller);
+                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
                         R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
                 info.bubbleBarExpandedView.initialize(
-                        controller, false /* isOverflow */, bubbleTaskView);
+                        expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView);
             }
 
             if (!populateCommonInfo(info, c, b, iconFactory)) {
@@ -191,8 +204,13 @@
 
         @VisibleForTesting
         @Nullable
-        public static BubbleViewInfo populate(Context c, BubbleController controller,
-                BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
+        public static BubbleViewInfo populate(Context c,
+                BubbleExpandedViewManager expandedViewManager,
+                BubbleTaskViewFactory taskViewFactory,
+                BubblePositioner positioner,
+                BubbleStackView stackView,
+                BubbleIconFactory iconFactory,
+                Bubble b,
                 boolean skipInflation) {
             BubbleViewInfo info = new BubbleViewInfo();
 
@@ -201,13 +219,14 @@
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.imageView = (BadgedImageView) inflater.inflate(
                         R.layout.bubble_view, stackView, false /* attachToRoot */);
-                info.imageView.initialize(controller.getPositioner());
+                info.imageView.initialize(positioner);
 
-                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(c, controller);
+                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
                 info.expandedView = (BubbleExpandedView) inflater.inflate(
                         R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
                 info.expandedView.initialize(
-                        controller, stackView, false /* isOverflow */, bubbleTaskView);
+                        expandedViewManager, stackView, positioner, false /* isOverflow */,
+                        bubbleTaskView);
             }
 
             if (!populateCommonInfo(info, c, b, iconFactory)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 73a9cf4..ebb8e3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -36,8 +36,9 @@
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
-import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
 import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
+import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleTaskView;
 import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -45,11 +46,7 @@
 
 import java.util.function.Supplier;
 
-/**
- * Expanded view of a bubble when it's part of the bubble bar.
- *
- * {@link BubbleController#isShowingAsBubbleBar()}
- */
+/** Expanded view of a bubble when it's part of the bubble bar. */
 public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskViewHelper.Listener {
     /**
      * The expanded view listener notifying the {@link BubbleBarLayerView} about the internal
@@ -67,7 +64,7 @@
     private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
     private static final int INVALID_TASK_ID = -1;
 
-    private BubbleController mController;
+    private BubbleExpandedViewManager mManager;
     private boolean mIsOverflow;
     private BubbleTaskViewHelper mBubbleTaskViewHelper;
     private BubbleBarMenuViewController mMenuViewController;
@@ -133,20 +130,22 @@
         mMenuViewController.hideMenu(false /* animated */);
     }
 
-    /** Set the BubbleController on the view, must be called before doing anything else. */
-    public void initialize(BubbleController controller, boolean isOverflow,
+    /** Initializes the view, must be called before doing anything else. */
+    public void initialize(BubbleExpandedViewManager expandedViewManager,
+            BubblePositioner positioner,
+            boolean isOverflow,
             @Nullable BubbleTaskView bubbleTaskView) {
-        mController = controller;
+        mManager = expandedViewManager;
         mIsOverflow = isOverflow;
 
         if (mIsOverflow) {
             mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
                     R.layout.bubble_overflow_container, null /* root */);
-            mOverflowView.setBubbleController(mController);
+            mOverflowView.initialize(expandedViewManager, positioner);
             addView(mOverflowView);
         } else {
             mTaskView = bubbleTaskView.getTaskView();
-            mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
+            mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager,
                     /* listener= */ this, bubbleTaskView,
                     /* viewParent= */ this);
             if (mTaskView.getParent() != null) {
@@ -178,13 +177,13 @@
 
             @Override
             public void onOpenAppSettings(Bubble bubble) {
-                mController.collapseStack();
+                mManager.collapseStack();
                 mContext.startActivityAsUser(bubble.getSettingsIntent(mContext), bubble.getUser());
             }
 
             @Override
             public void onDismissBubble(Bubble bubble) {
-                mController.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED);
+                mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED);
             }
         });
         mHandleView.setOnClickListener(view -> {
@@ -279,7 +278,7 @@
         if (mMenuViewController.isMenuVisible()) {
             mMenuViewController.hideMenu(/* animated = */ true);
         } else {
-            mController.collapseStack();
+            mManager.collapseStack();
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index f175775..5f9195a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,19 +15,13 @@
  */
 package com.android.wm.shell.pip.phone;
 
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.os.Looper;
 import android.view.BatchedInputEventReceiver;
@@ -41,7 +35,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.policy.TaskResizingAlgorithm;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -53,7 +46,6 @@
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -77,7 +69,6 @@
     private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
     private final int mDisplayId;
     private final ShellExecutor mMainExecutor;
-    private final Region mTmpRegion = new Region();
 
     private final PointF mDownPoint = new PointF();
     private final PointF mDownSecondPoint = new PointF();
@@ -88,24 +79,15 @@
     private final Rect mLastResizeBounds = new Rect();
     private final Rect mUserResizeBounds = new Rect();
     private final Rect mDownBounds = new Rect();
-    private final Rect mDragCornerSize = new Rect();
-    private final Rect mTmpTopLeftCorner = new Rect();
-    private final Rect mTmpTopRightCorner = new Rect();
-    private final Rect mTmpBottomLeftCorner = new Rect();
-    private final Rect mTmpBottomRightCorner = new Rect();
-    private final Rect mDisplayBounds = new Rect();
-    private final Function<Rect, Rect> mMovementBoundsSupplier;
     private final Runnable mUpdateMovementBoundsRunnable;
     private final Consumer<Rect> mUpdateResizeBoundsCallback;
 
-    private int mDelta;
     private float mTouchSlop;
 
     private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
     private boolean mEnablePinchResize;
-    private boolean mEnableDragCornerResize;
     private boolean mIsSysUiStateValid;
     private boolean mThresholdCrossed;
     private boolean mOngoingPinchToResize = false;
@@ -123,7 +105,7 @@
             PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
             PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
             PipDismissTargetHandler pipDismissTargetHandler,
-            Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
+            Runnable updateMovementBoundsRunnable,
             PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
             ShellExecutor mainExecutor) {
         mContext = context;
@@ -135,7 +117,6 @@
         mPipTouchState = pipTouchState;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipDismissTargetHandler = pipDismissTargetHandler;
-        mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mPhonePipMenuController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
@@ -171,20 +152,9 @@
     }
 
     private void reloadResources() {
-        final Resources res = mContext.getResources();
-        mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
-        mEnableDragCornerResize = res.getBoolean(R.bool.config_pipEnableDragCornerResize);
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
     }
 
-    private void resetDragCorners() {
-        mDragCornerSize.set(0, 0, mDelta, mDelta);
-        mTmpTopLeftCorner.set(mDragCornerSize);
-        mTmpTopRightCorner.set(mDragCornerSize);
-        mTmpBottomLeftCorner.set(mDragCornerSize);
-        mTmpBottomRightCorner.set(mDragCornerSize);
-    }
-
     private void disposeInputChannel() {
         if (mInputEventReceiver != null) {
             mInputEventReceiver.dispose();
@@ -232,7 +202,7 @@
 
     @VisibleForTesting
     void onInputEvent(InputEvent ev) {
-        if (!mEnableDragCornerResize && !mEnablePinchResize) {
+        if (!mEnablePinchResize) {
             // No need to handle anything if neither form of resizing is enabled.
             return;
         }
@@ -260,8 +230,6 @@
 
             if (mEnablePinchResize && mOngoingPinchToResize) {
                 onPinchResize(mv);
-            } else if (mEnableDragCornerResize) {
-                onDragCornerResize(mv);
             }
         }
     }
@@ -273,48 +241,6 @@
         return mCtrlType != CTRL_NONE || mOngoingPinchToResize;
     }
 
-    /**
-     * Check whether the current x,y coordinate is within the region in which drag-resize should
-     * start.
-     * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
-     * overlaps with the PIP window while the rest goes outside of the PIP window.
-     *  _ _           _ _
-     * |_|_|_________|_|_|
-     * |_|_|         |_|_|
-     *   |     PIP     |
-     *   |   WINDOW    |
-     *  _|_           _|_
-     * |_|_|_________|_|_|
-     * |_|_|         |_|_|
-     */
-    public boolean isWithinDragResizeRegion(int x, int y) {
-        if (!mEnableDragCornerResize) {
-            return false;
-        }
-
-        final Rect currentPipBounds = mPipBoundsState.getBounds();
-        if (currentPipBounds == null) {
-            return false;
-        }
-        resetDragCorners();
-        mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2,
-                currentPipBounds.top - mDelta /  2);
-        mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2,
-                currentPipBounds.top - mDelta /  2);
-        mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2,
-                currentPipBounds.bottom - mDelta /  2);
-        mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2,
-                currentPipBounds.bottom - mDelta /  2);
-
-        mTmpRegion.setEmpty();
-        mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION);
-        mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION);
-        mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION);
-        mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION);
-
-        return mTmpRegion.contains(x, y);
-    }
-
     public boolean isUsingPinchToZoom() {
         return mEnablePinchResize;
     }
@@ -325,62 +251,17 @@
 
     public boolean willStartResizeGesture(MotionEvent ev) {
         if (isInValidSysUiState()) {
-            switch (ev.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
-                        return true;
-                    }
-                    break;
-
-                case MotionEvent.ACTION_POINTER_DOWN:
-                    if (mEnablePinchResize && ev.getPointerCount() == 2) {
-                        onPinchResize(ev);
-                        mOngoingPinchToResize = mAllowGesture;
-                        return mAllowGesture;
-                    }
-                    break;
-
-                default:
-                    break;
+            if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+                if (mEnablePinchResize && ev.getPointerCount() == 2) {
+                    onPinchResize(ev);
+                    mOngoingPinchToResize = mAllowGesture;
+                    return mAllowGesture;
+                }
             }
         }
         return false;
     }
 
-    private void setCtrlType(int x, int y) {
-        final Rect currentPipBounds = mPipBoundsState.getBounds();
-
-        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
-
-        mDisplayBounds.set(movementBounds.left,
-                movementBounds.top,
-                movementBounds.right + currentPipBounds.width(),
-                movementBounds.bottom + currentPipBounds.height());
-
-        if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
-                && currentPipBounds.left != mDisplayBounds.left) {
-            mCtrlType |= CTRL_LEFT;
-            mCtrlType |= CTRL_TOP;
-        }
-        if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
-                && currentPipBounds.right != mDisplayBounds.right) {
-            mCtrlType |= CTRL_RIGHT;
-            mCtrlType |= CTRL_TOP;
-        }
-        if (mTmpBottomRightCorner.contains(x, y)
-                && currentPipBounds.bottom != mDisplayBounds.bottom
-                && currentPipBounds.right != mDisplayBounds.right) {
-            mCtrlType |= CTRL_RIGHT;
-            mCtrlType |= CTRL_BOTTOM;
-        }
-        if (mTmpBottomLeftCorner.contains(x, y)
-                && currentPipBounds.bottom != mDisplayBounds.bottom
-                && currentPipBounds.left != mDisplayBounds.left) {
-            mCtrlType |= CTRL_LEFT;
-            mCtrlType |= CTRL_BOTTOM;
-        }
-    }
-
     private boolean isInValidSysUiState() {
         return mIsSysUiStateValid;
     }
@@ -457,59 +338,6 @@
         }
     }
 
-    private void onDragCornerResize(MotionEvent ev) {
-        int action = ev.getActionMasked();
-        float x = ev.getX();
-        float y = ev.getY() - mOhmOffset;
-        if (action == MotionEvent.ACTION_DOWN) {
-            mLastResizeBounds.setEmpty();
-            mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
-            if (mAllowGesture) {
-                setCtrlType((int) x, (int) y);
-                mDownPoint.set(x, y);
-                mDownBounds.set(mPipBoundsState.getBounds());
-            }
-        } else if (mAllowGesture) {
-            switch (action) {
-                case MotionEvent.ACTION_POINTER_DOWN:
-                    // We do not support multi touch for resizing via drag
-                    mAllowGesture = false;
-                    break;
-                case MotionEvent.ACTION_MOVE:
-                    // Capture inputs
-                    if (!mThresholdCrossed
-                            && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
-                        mThresholdCrossed = true;
-                        // Reset the down to begin resizing from this point
-                        mDownPoint.set(x, y);
-                        mInputMonitor.pilferPointers();
-                    }
-                    if (mThresholdCrossed) {
-                        if (mPhonePipMenuController.isMenuVisible()) {
-                            mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE,
-                                    false /* resize */);
-                        }
-                        final Rect currentPipBounds = mPipBoundsState.getBounds();
-                        mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
-                                mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
-                                mMinSize.y, mMaxSize, true,
-                                mDownBounds.width() > mDownBounds.height()));
-                        mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
-                                mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
-                                true /* useCurrentSize */);
-                        mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
-                                null);
-                        mPipBoundsState.setHasUserResizedPip(true);
-                    }
-                    break;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    finishResize();
-                    break;
-            }
-        }
-    }
-
     private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
         final int leftEdge = bounds.left;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 81705e2..11c356d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -212,7 +212,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
                         mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
-                        this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
+                        this::updateMovementBounds, pipUiEventLogger,
                         menuController, mainExecutor);
         mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
                 mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index f5b0174..094af96 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -45,18 +44,22 @@
 
     private TestableBubblePositioner mPositioner;
     private BubbleOverflow mOverflow;
+    private BubbleExpandedViewManager mExpandedViewManager;
 
     @Mock
     private BubbleController mBubbleController;
+    @Mock
+    private BubbleStackView mBubbleStackView;
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController);
         mPositioner = new TestableBubblePositioner(mContext,
                 mContext.getSystemService(WindowManager.class));
         when(mBubbleController.getPositioner()).thenReturn(mPositioner);
-        when(mBubbleController.getStackView()).thenReturn(mock(BubbleStackView.class));
+        when(mBubbleController.getStackView()).thenReturn(mBubbleStackView);
 
         mOverflow = new BubbleOverflow(mContext, mPositioner);
     }
@@ -65,7 +68,7 @@
     public void test_initialize_forStack() {
         assertThat(mOverflow.getExpandedView()).isNull();
 
-        mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
+        mOverflow.initialize(mExpandedViewManager, mBubbleStackView, mPositioner);
 
         assertThat(mOverflow.getExpandedView()).isNotNull();
         assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY);
@@ -74,7 +77,7 @@
 
     @Test
     public void test_initialize_forBubbleBar() {
-        mOverflow.initialize(mBubbleController, /* forBubbleBar= */ true);
+        mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner);
 
         assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
         assertThat(mOverflow.getExpandedView()).isNull();
@@ -82,11 +85,10 @@
 
     @Test
     public void test_cleanUpExpandedState() {
-        mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
+        mOverflow.initialize(mExpandedViewManager, mBubbleStackView, mPositioner);
         assertThat(mOverflow.getExpandedView()).isNotNull();
 
         mOverflow.cleanUpExpandedState();
         assertThat(mOverflow.getExpandedView()).isNull();
     }
-
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
index 1668e37..ae39fbc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
@@ -44,6 +44,7 @@
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.taskview.TaskView
 import com.android.wm.shell.taskview.TaskViewTransitions
 import com.android.wm.shell.transition.Transitions
 import com.google.common.truth.Truth.assertThat
@@ -55,6 +56,7 @@
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
+import java.util.concurrent.Executor
 
 /** Tests for loading / inflating views & icons for a bubble. */
 @SmallTest
@@ -65,11 +67,16 @@
     private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
     private lateinit var iconFactory: BubbleIconFactory
     private lateinit var bubble: Bubble
-
     private lateinit var bubbleController: BubbleController
     private lateinit var mainExecutor: ShellExecutor
     private lateinit var bubbleStackView: BubbleStackView
     private lateinit var bubbleBarLayerView: BubbleBarLayerView
+    private lateinit var bubblePositioner: BubblePositioner
+    private lateinit var expandedViewManager: BubbleExpandedViewManager
+
+    private val bubbleTaskViewFactory = BubbleTaskViewFactory {
+        BubbleTaskView(mock<TaskView>(), mock<Executor>())
+    }
 
     @Before
     fun setup() {
@@ -88,7 +95,7 @@
         val shellInit = ShellInit(mainExecutor)
         val shellCommandHandler = ShellCommandHandler()
         val shellController = ShellController(context, shellInit, shellCommandHandler, mainExecutor)
-        val bubblePositioner = BubblePositioner(context, windowManager)
+        bubblePositioner = BubblePositioner(context, windowManager)
         val bubbleData =
             BubbleData(
                 context,
@@ -143,6 +150,7 @@
                 bubbleController,
                 mainExecutor
             )
+        expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
         bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData)
     }
 
@@ -152,7 +160,9 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populate(
                 context,
-                bubbleController,
+                expandedViewManager,
+                bubbleTaskViewFactory,
+                bubblePositioner,
                 bubbleStackView,
                 iconFactory,
                 bubble,
@@ -178,7 +188,9 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                 context,
-                bubbleController,
+                expandedViewManager,
+                bubbleTaskViewFactory,
+                bubblePositioner,
                 bubbleBarLayerView,
                 iconFactory,
                 bubble,
@@ -212,7 +224,9 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                 context,
-                bubbleController,
+                expandedViewManager,
+                bubbleTaskViewFactory,
+                bubblePositioner,
                 bubbleBarLayerView,
                 iconFactory,
                 bubble,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 9719ba8..cc726cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -121,7 +121,7 @@
         mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
                 mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
                 mPipDismissTargetHandler,
-                (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+                () -> {}, mPipUiEventLogger, mPhonePipMenuController,
                 mMainExecutor) {
             @Override
             public void pilferPointers() {
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
index cc79cba..5e73e76 100644
--- a/libs/hwui/apex/android_paint.cpp
+++ b/libs/hwui/apex/android_paint.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include "android/graphics/paint.h"
+#include <SkBlendMode.h>
+#include <SkImageFilter.h>
+#include <hwui/Paint.h>
 
 #include "TypeCast.h"
-
-#include <hwui/Paint.h>
-#include <SkBlendMode.h>
+#include "android/graphics/paint.h"
+#include "include/effects/SkImageFilters.h"
 
 using namespace android;
 
@@ -43,6 +44,22 @@
     }
 }
 
+static sk_sp<SkImageFilter> convertImageFilter(AImageFilter imageFilter) {
+    switch (imageFilter) {
+        case AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON:
+            // Material Elevation Level 1 Drop Shadow.
+            sk_sp<SkImageFilter> key_shadow = SkImageFilters::DropShadow(
+                    0.0f, 1.0f, 2.0f, 2.0f, SkColorSetARGB(0x4D, 0x00, 0x00, 0x00), nullptr);
+            sk_sp<SkImageFilter> ambient_shadow = SkImageFilters::DropShadow(
+                    0.0f, 1.0f, 3.0f, 3.0f, SkColorSetARGB(0x26, 0x00, 0x00, 0x00), nullptr);
+            return SkImageFilters::Compose(ambient_shadow, key_shadow);
+    }
+}
+
 void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) {
     TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode));
 }
+
+void APaint_setImageFilter(APaint* paint, AImageFilter imageFilter) {
+    TypeCast::toPaint(paint)->setImageFilter(convertImageFilter(imageFilter));
+}
diff --git a/libs/hwui/apex/include/android/graphics/paint.h b/libs/hwui/apex/include/android/graphics/paint.h
index 058db8d..36b7575 100644
--- a/libs/hwui/apex/include/android/graphics/paint.h
+++ b/libs/hwui/apex/include/android/graphics/paint.h
@@ -26,6 +26,14 @@
  */
 typedef struct APaint APaint;
 
+/**
+ * Predefined Image filter type.
+ */
+enum AImageFilter {
+    /** Drop shadow image filter for PointerIcons. */
+    AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON = 0,
+};
+
 /** Bitmap pixel format. */
 enum ABlendMode {
     /** replaces destination with zero: fully transparent */
@@ -42,6 +50,8 @@
 
 ANDROID_API void APaint_setBlendMode(APaint* paint, ABlendMode blendMode);
 
+ANDROID_API void APaint_setImageFilter(APaint* paint, AImageFilter imageFilter);
+
 __END_DECLS
 
 #ifdef	__cplusplus
@@ -54,6 +64,10 @@
 
         void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); }
 
+        void setImageFilter(AImageFilter imageFilter) {
+            APaint_setImageFilter(mPaint, imageFilter);
+        }
+
         const APaint& get() const { return *mPaint; }
 
     private:
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index fdb2373..d03ceb4 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -32,6 +32,7 @@
     APaint_createPaint;
     APaint_destroyPaint;
     APaint_setBlendMode;
+    APaint_setImageFilter;
     ARegionIterator_acquireIterator;
     ARegionIterator_releaseIterator;
     ARegionIterator_isComplex;
diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp
index b7e51e2..59e36e4 100644
--- a/libs/input/SpriteIcon.cpp
+++ b/libs/input/SpriteIcon.cpp
@@ -34,6 +34,9 @@
 
     graphics::Paint paint;
     paint.setBlendMode(ABLEND_MODE_SRC);
+    if (drawNativeDropShadow) {
+        paint.setImageFilter(AIMAGE_FILTER_DROP_SHADOW_FOR_POINTER_ICON);
+    }
 
     graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace());
     canvas.drawBitmap(bitmap, 0, 0, &paint);
diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h
index 5f085bb..9e6cc81 100644
--- a/libs/input/SpriteIcon.h
+++ b/libs/input/SpriteIcon.h
@@ -29,16 +29,22 @@
 struct SpriteIcon {
     inline SpriteIcon() : style(PointerIconStyle::TYPE_NULL), hotSpotX(0), hotSpotY(0) {}
     inline SpriteIcon(const graphics::Bitmap& bitmap, PointerIconStyle style, float hotSpotX,
-                      float hotSpotY)
-          : bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) {}
+                      float hotSpotY, bool drawNativeDropShadow)
+          : bitmap(bitmap),
+            style(style),
+            hotSpotX(hotSpotX),
+            hotSpotY(hotSpotY),
+            drawNativeDropShadow(drawNativeDropShadow) {}
 
     graphics::Bitmap bitmap;
     PointerIconStyle style;
     float hotSpotX;
     float hotSpotY;
+    bool drawNativeDropShadow;
 
     inline SpriteIcon copy() const {
-        return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY);
+        return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY,
+                          drawNativeDropShadow);
     }
 
     inline void reset() {
@@ -46,6 +52,7 @@
         style = PointerIconStyle::TYPE_NULL;
         hotSpotX = 0;
         hotSpotY = 0;
+        drawNativeDropShadow = false;
     }
 
     inline bool isValid() const { return bitmap.isValid() && !bitmap.isEmpty(); }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e9ba779..8f3f82e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5513,6 +5513,26 @@
 
     /**
      * @hide
+     * @return All currently registered audio policy mixes.
+     */
+    @TestApi
+    @FlaggedApi(android.media.audiopolicy.Flags.FLAG_AUDIO_MIX_TEST_API)
+    @NonNull
+    public List<android.media.audiopolicy.AudioMix> getRegisteredPolicyMixes() {
+        if (!android.media.audiopolicy.Flags.audioMixTestApi()) {
+            return Collections.emptyList();
+        }
+
+        final IAudioService service = getService();
+        try {
+            return service.getRegisteredPolicyMixes();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
      * @return true if an AudioPolicy was previously registered
      */
     @TestApi
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index f73be35f..293c561 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1984,6 +1984,9 @@
     public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
 
     /** @hide */
+    public static native int getRegisteredPolicyMixes(@NonNull List<AudioMix> devices);
+
+    /** @hide */
     public static native int updatePolicyMixes(
             AudioMix[] mixes,
             AudioMixingRule[] updatedMixingRules);
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 40b0e3e..4f1a8ee 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -18,6 +18,7 @@
 
 import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
 
+import android.annotation.DurationMillisLong;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -112,17 +113,11 @@
      */
     public static final int FADE_STATE_ENABLED_DEFAULT = 1;
 
-    /**
-     * Defines the enabled state with Automotive specific configurations
-     */
-    public static final int FADE_STATE_ENABLED_AUTO = 2;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = false, prefix = "FADE_STATE", value = {
             FADE_STATE_DISABLED,
             FADE_STATE_ENABLED_DEFAULT,
-            FADE_STATE_ENABLED_AUTO,
     })
     public @interface FadeStateEnum {}
 
@@ -143,7 +138,14 @@
      * @see #getFadeOutDurationForAudioAttributes(AudioAttributes)
      * @see #getFadeInDurationForAudioAttributes(AudioAttributes)
      */
-    public static final long DURATION_NOT_SET = 0;
+    public static final @DurationMillisLong long DURATION_NOT_SET = 0;
+
+    /** Defines the default fade out duration */
+    private static final @DurationMillisLong long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
+
+    /** Defines the default fade in duration */
+    private static final @DurationMillisLong long DEFAULT_FADE_IN_DURATION_MS = 1_000;
+
     /** Map of Usage to Fade volume shaper configs wrapper */
     private final SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap;
     /** Map of AudioAttributes to Fade volume shaper configs wrapper */
@@ -161,14 +163,15 @@
     /** fade state */
     private final @FadeStateEnum int mFadeState;
     /** fade out duration from builder - used for creating default fade out volume shaper */
-    private final long mFadeOutDurationMillis;
+    private final @DurationMillisLong long mFadeOutDurationMillis;
     /** fade in duration from builder - used for creating default fade in volume shaper */
-    private final long mFadeInDurationMillis;
+    private final @DurationMillisLong long mFadeInDurationMillis;
     /** delay after which the offending players are faded back in */
-    private final long mFadeInDelayForOffendersMillis;
+    private final @DurationMillisLong long mFadeInDelayForOffendersMillis;
 
-    private FadeManagerConfiguration(int fadeState, long fadeOutDurationMillis,
-            long fadeInDurationMillis, long offendersFadeInDelayMillis,
+    private FadeManagerConfiguration(int fadeState, @DurationMillisLong long fadeOutDurationMillis,
+            @DurationMillisLong long fadeInDurationMillis,
+            @DurationMillisLong long offendersFadeInDelayMillis,
             @NonNull SparseArray<FadeVolumeShaperConfigsWrapper> usageToFadeWrapperMap,
             @NonNull ArrayMap<AudioAttributes, FadeVolumeShaperConfigsWrapper> attrToFadeWrapperMap,
             @NonNull IntArray fadeableUsages, @NonNull IntArray unfadeableContentTypes,
@@ -196,8 +199,6 @@
 
     /**
      * Get the fade state
-     *
-     * @return one of the {@link FadeStateEnum} state
      */
     @FadeStateEnum
     public int getFadeState() {
@@ -207,7 +208,7 @@
     /**
      * Get the list of usages that can be faded
      *
-     * @return list of {@link android.media.AudioAttributes.AttributeUsage} that shall be faded
+     * @return list of {@link android.media.AudioAttributes usages} that shall be faded
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
     @NonNull
@@ -217,10 +218,10 @@
     }
 
     /**
-     * Get the list of {@link android.media.AudioPlaybackConfiguration.PlayerType player types}
-     * that cannot be faded
+     * Get the list of {@link android.media.AudioPlaybackConfiguration player types} that can be
+     * faded
      *
-     * @return list of {@link android.media.AudioPlaybackConfiguration.PlayerType}
+     * @return list of {@link android.media.AudioPlaybackConfiguration player types}
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
     @NonNull
@@ -230,10 +231,9 @@
     }
 
     /**
-     * Get the list of {@link android.media.AudioAttributes.AttributeContentType content types}
-     * that cannot be faded
+     * Get the list of {@link android.media.AudioAttributes content types} that can be faded
      *
-     * @return list of {@link android.media.AudioAttributes.AttributeContentType}
+     * @return list of {@link android.media.AudioAttributes content types}
      * @throws IllegalStateExceptionif if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
     @NonNull
@@ -267,15 +267,15 @@
     }
 
     /**
-     * Get the duration used to fade out players with
-     * {@link android.media.AudioAttributes.AttributeUsage}
+     * Get the duration used to fade out players with {@link android.media.AudioAttributes usage}
      *
-     * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+     * @param usage the {@link android.media.AudioAttributes usage}
      * @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise
      * @throws IllegalArgumentException if the usage is invalid
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
-    public long getFadeOutDurationForUsage(int usage) {
+    @DurationMillisLong
+    public long getFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
         ensureFadingIsEnabled();
         validateUsage(usage);
         return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -283,15 +283,15 @@
     }
 
     /**
-     * Get the duration used to fade in players with
-     * {@link android.media.AudioAttributes.AttributeUsage}
+     * Get the duration used to fade in players with {@link android.media.AudioAttributes usage}
      *
-     * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+     * @param usage the {@link android.media.AudioAttributes usage}
      * @return duration in milliseconds if set for the usage or {@link #DURATION_NOT_SET} otherwise
      * @throws IllegalArgumentException if the usage is invalid
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
-    public long getFadeInDurationForUsage(int usage) {
+    @DurationMillisLong
+    public long getFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
         ensureFadingIsEnabled();
         validateUsage(usage);
         return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -300,16 +300,17 @@
 
     /**
      * Get the {@link android.media.VolumeShaper.Configuration} used to fade out players with
-     * {@link android.media.AudioAttributes.AttributeUsage}
+     * {@link android.media.AudioAttributes usage}
      *
-     * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+     * @param usage the {@link android.media.AudioAttributes usage}
      * @return {@link android.media.VolumeShaper.Configuration} if set for the usage or
-     * {@code null} otherwise
+     *     {@code null} otherwise
      * @throws IllegalArgumentException if the usage is invalid
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
     @Nullable
-    public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int usage) {
+    public VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(
+            @AudioAttributes.AttributeUsage int usage) {
         ensureFadingIsEnabled();
         validateUsage(usage);
         return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage),
@@ -318,16 +319,17 @@
 
     /**
      * Get the {@link android.media.VolumeShaper.Configuration} used to fade in players with
-     * {@link android.media.AudioAttributes.AttributeUsage}
+     * {@link android.media.AudioAttributes usage}
      *
-     * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of player
+     * @param usage the {@link android.media.AudioAttributes usage}
      * @return {@link android.media.VolumeShaper.Configuration} if set for the usage or
-     * {@code null} otherwise
+     *     {@code null} otherwise
      * @throws IllegalArgumentException if the usage is invalid
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
     @Nullable
-    public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int usage) {
+    public VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(
+            @AudioAttributes.AttributeUsage int usage) {
         ensureFadingIsEnabled();
         validateUsage(usage);
         return getVolumeShaperConfigFromWrapper(mUsageToFadeWrapperMap.get(usage),
@@ -339,10 +341,11 @@
      *
      * @param audioAttributes {@link android.media.AudioAttributes}
      * @return duration in milliseconds if set for the audio attributes or
-     * {@link #DURATION_NOT_SET} otherwise
+     *     {@link #DURATION_NOT_SET} otherwise
      * @throws NullPointerException if the audio attributes is {@code null}
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
+    @DurationMillisLong
     public long getFadeOutDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
         ensureFadingIsEnabled();
         return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -354,10 +357,11 @@
      *
      * @param audioAttributes {@link android.media.AudioAttributes}
      * @return duration in milliseconds if set for the audio attributes or
-     * {@link #DURATION_NOT_SET} otherwise
+     *     {@link #DURATION_NOT_SET} otherwise
      * @throws NullPointerException if the audio attributes is {@code null}
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
+    @DurationMillisLong
     public long getFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
         ensureFadingIsEnabled();
         return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -370,7 +374,7 @@
      *
      * @param audioAttributes {@link android.media.AudioAttributes}
      * @return {@link android.media.VolumeShaper.Configuration} if set for the audio attribute or
-     * {@code null} otherwise
+     *     {@code null} otherwise
      * @throws NullPointerException if the audio attributes is {@code null}
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
@@ -389,7 +393,7 @@
      *
      * @param audioAttributes {@link android.media.AudioAttributes}
      * @return {@link android.media.VolumeShaper.Configuration} used for fading in if set for the
-     * audio attribute or {@code null} otherwise
+     *     audio attribute or {@code null} otherwise
      * @throws NullPointerException if the audio attributes is {@code null}
      * @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
      */
@@ -407,7 +411,7 @@
      * configurations are defined
      *
      * @return list of {@link android.media.AudioAttributes} with valid volume shaper configs or
-     * empty list if none set.
+     *     empty list if none set.
      */
     @NonNull
     public List<AudioAttributes> getAudioAttributesWithVolumeShaperConfigs() {
@@ -417,8 +421,14 @@
     /**
      * Get the delay after which the offending players are faded back in
      *
+     * Players are categorized as offending if they do not honor audio focus state changes. For
+     * example - when an app loses audio focus, it is expected that the app stops any active
+     * player in favor of the app(s) that gained audio focus. However, if the app do not stop the
+     * audio playback, such players are termed as offenders.
+     *
      * @return delay in milliseconds
      */
+    @DurationMillisLong
     public long getFadeInDelayForOffenders() {
         return mFadeInDelayForOffendersMillis;
     }
@@ -435,8 +445,9 @@
     /**
      * Query if the usage is fadeable
      *
-     * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
-     * @return {@code true} if usage is fadeable, {@code false} otherwise
+     * @param usage the {@link android.media.AudioAttributes usage}
+     * @return {@code true} if usage is fadeable, {@code false}  when the fade state is set to
+     *     {@link #FADE_STATE_DISABLED} or if the usage is not fadeable.
      */
     public boolean isUsageFadeable(@AudioAttributes.AttributeUsage int usage) {
         if (!isFadeEnabled()) {
@@ -448,9 +459,9 @@
     /**
      * Query if the content type is unfadeable
      *
-     * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+     * @param contentType the {@link android.media.AudioAttributes content type}
      * @return {@code true} if content type is unfadeable or if fade state is set to
-     * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+     *     {@link #FADE_STATE_DISABLED}, {@code false} otherwise
      */
     public boolean isContentTypeUnfadeable(@AudioAttributes.AttributeContentType int contentType) {
         if (!isFadeEnabled()) {
@@ -462,11 +473,11 @@
     /**
      * Query if the player type is unfadeable
      *
-     * @param playerType the {@link android.media.AudioPlaybackConfiguration} player type
+     * @param playerType the {@link android.media.AudioPlaybackConfiguration player type}
      * @return {@code true} if player type is unfadeable or if fade state is set to
-     * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+     *     {@link #FADE_STATE_DISABLED}, {@code false} otherwise
      */
-    public boolean isPlayerTypeUnfadeable(int playerType) {
+    public boolean isPlayerTypeUnfadeable(@AudioPlaybackConfiguration.PlayerType int playerType) {
         if (!isFadeEnabled()) {
             return true;
         }
@@ -478,7 +489,7 @@
      *
      * @param audioAttributes the {@link android.media.AudioAttributes}
      * @return {@code true} if audio attributes is unfadeable or if fade state is set to
-     * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+     *     {@link #FADE_STATE_DISABLED}, {@code false} otherwise
      * @throws NullPointerException if the audio attributes is {@code null}
      */
     public boolean isAudioAttributesUnfadeable(@NonNull AudioAttributes audioAttributes) {
@@ -494,7 +505,7 @@
      *
      * @param uid the uid of application
      * @return {@code true} if uid is unfadeable or if fade state is set to
-     * {@link #FADE_STATE_DISABLED}, {@code false} otherwise
+     *     {@link #FADE_STATE_DISABLED}, {@code false} otherwise
      */
     public boolean isUidUnfadeable(int uid) {
         if (!isFadeEnabled()) {
@@ -503,6 +514,20 @@
         return mUnfadeableUids.contains(uid);
     }
 
+    /**
+     * Returns the default fade out duration (in milliseconds)
+     */
+    public static @DurationMillisLong long getDefaultFadeOutDurationMillis() {
+        return DEFAULT_FADE_OUT_DURATION_MS;
+    }
+
+    /**
+     * Returns the default fade in duration (in milliseconds)
+     */
+    public static @DurationMillisLong long getDefaultFadeInDurationMillis() {
+        return DEFAULT_FADE_IN_DURATION_MS;
+    }
+
     @Override
     public String toString() {
         return "FadeManagerConfiguration { fade state = " + fadeStateToString(mFadeState)
@@ -520,7 +545,7 @@
     /**
      * Convert fade state into a human-readable string
      *
-     * @param fadeState one of the fade state in {@link FadeStateEnum}
+     * @param fadeState one of {@link #FADE_STATE_DISABLED} or {@link #FADE_STATE_ENABLED_DEFAULT}
      * @return human-readable string
      * @hide
      */
@@ -531,8 +556,6 @@
                 return "FADE_STATE_DISABLED";
             case FADE_STATE_ENABLED_DEFAULT:
                 return "FADE_STATE_ENABLED_DEFAULT";
-            case FADE_STATE_ENABLED_AUTO:
-                return "FADE_STATE_ENABLED_AUTO";
             default:
                 return "unknown fade state: " + fadeState;
         }
@@ -712,9 +735,9 @@
      *
      * <p><b>Notes:</b>
      * <ul>
-     *     <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT} or
-     *     {@link #FADE_STATE_ENABLED_AUTO}, the builder expects at least one valid usage to be
-     *     set/added. Failure to do so will result in an exception during {@link #build()}</li>
+     *     <li>When fade state is set to {@link #FADE_STATE_ENABLED_DEFAULT}, the builder expects at
+     *     least one valid usage to be set/added. Failure to do so will result in an exception
+     *     during {@link #build()}</li>
      *     <li>Every usage added to the fadeable list should have corresponding volume shaper
      *     configs defined. This can be achieved by setting either the duration or volume shaper
      *     config through {@link #setFadeOutDurationForUsage(int, long)} or
@@ -741,11 +764,6 @@
         private static final long IS_FADEABLE_USAGES_FIELD_SET = 1 << 1;
         private static final long IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET = 1 << 2;
 
-        /** duration of the fade out curve */
-        private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
-        /** duration of the fade in curve */
-        private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000;
-
         /**
          * delay after which a faded out player will be faded back in. This will be heard by the
          * user only in the case of unmuting players that didn't respect audio focus and didn't
@@ -771,9 +789,10 @@
         });
 
         private int mFadeState = FADE_STATE_ENABLED_DEFAULT;
-        private long mFadeInDelayForOffendersMillis = DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
-        private long mFadeOutDurationMillis;
-        private long mFadeInDurationMillis;
+        private @DurationMillisLong long mFadeInDelayForOffendersMillis =
+                DEFAULT_DELAY_FADE_IN_OFFENDERS_MS;
+        private @DurationMillisLong long mFadeOutDurationMillis;
+        private @DurationMillisLong long mFadeInDurationMillis;
         private long mBuilderFieldsSet;
         private SparseArray<FadeVolumeShaperConfigsWrapper> mUsageToFadeWrapperMap =
                 new SparseArray<>();
@@ -787,7 +806,8 @@
         private List<AudioAttributes> mUnfadeableAudioAttributes = new ArrayList<>();
 
         /**
-         * Constructs a new Builder with default fade out and fade in durations
+         * Constructs a new Builder with {@link #DEFAULT_FADE_OUT_DURATION_MS} and
+         * {@link #DEFAULT_FADE_IN_DURATION_MS} durations.
          */
         public Builder() {
             mFadeOutDurationMillis = DEFAULT_FADE_OUT_DURATION_MS;
@@ -800,7 +820,8 @@
          * @param fadeOutDurationMillis duration in milliseconds used for fading out
          * @param fadeInDurationMills duration in milliseconds used for fading in
          */
-        public Builder(long fadeOutDurationMillis, long fadeInDurationMills) {
+        public Builder(@DurationMillisLong long fadeOutDurationMillis,
+                @DurationMillisLong long fadeInDurationMills) {
             mFadeOutDurationMillis = fadeOutDurationMillis;
             mFadeInDurationMillis = fadeInDurationMills;
         }
@@ -830,7 +851,8 @@
         /**
          * Set the overall fade state
          *
-         * @param state one of the {@link FadeStateEnum} states
+         * @param state one of the {@link #FADE_STATE_DISABLED} or
+         *     {@link #FADE_STATE_ENABLED_DEFAULT} states
          * @return the same Builder instance
          * @throws IllegalArgumentException if the fade state is invalid
          * @see #getFadeState()
@@ -844,21 +866,22 @@
 
         /**
          * Set the {@link android.media.VolumeShaper.Configuration} used to fade out players with
-         * {@link android.media.AudioAttributes.AttributeUsage}
+         * {@link android.media.AudioAttributes usage}
          * <p>
          * This method accepts {@code null} for volume shaper config to clear a previously set
          * configuration (example, if set through
          * {@link #Builder(android.media.FadeManagerConfiguration)})
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+         * @param usage the {@link android.media.AudioAttributes usage} of target player
          * @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used
-         *                             to fade out players with usage
+         *     to fade out players with usage
          * @return the same Builder instance
          * @throws IllegalArgumentException if the usage is invalid
          * @see #getFadeOutVolumeShaperConfigForUsage(int)
          */
         @NonNull
-        public Builder setFadeOutVolumeShaperConfigForUsage(int usage,
+        public Builder setFadeOutVolumeShaperConfigForUsage(
+                @AudioAttributes.AttributeUsage int usage,
                 @Nullable VolumeShaper.Configuration fadeOutVShaperConfig) {
             validateUsage(usage);
             getFadeVolShaperConfigWrapperForUsage(usage)
@@ -869,21 +892,22 @@
 
         /**
          * Set the {@link android.media.VolumeShaper.Configuration} used to fade in players with
-         * {@link android.media.AudioAttributes.AttributeUsage}
+         * {@link android.media.AudioAttributes usage}
          * <p>
          * This method accepts {@code null} for volume shaper config to clear a previously set
          * configuration (example, if set through
          * {@link #Builder(android.media.FadeManagerConfiguration)})
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+         * @param usage the {@link android.media.AudioAttributes usage}
          * @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used
-         *                            to fade in players with usage
+         *     to fade in players with usage
          * @return the same Builder instance
          * @throws IllegalArgumentException if the usage is invalid
          * @see #getFadeInVolumeShaperConfigForUsage(int)
          */
         @NonNull
-        public Builder setFadeInVolumeShaperConfigForUsage(int usage,
+        public Builder setFadeInVolumeShaperConfigForUsage(
+                @AudioAttributes.AttributeUsage int usage,
                 @Nullable VolumeShaper.Configuration fadeInVShaperConfig) {
             validateUsage(usage);
             getFadeVolShaperConfigWrapperForUsage(usage)
@@ -894,7 +918,7 @@
 
         /**
          * Set the duration used for fading out players with
-         * {@link android.media.AudioAttributes.AttributeUsage}
+         * {@link android.media.AudioAttributes usage}
          * <p>
          * A Volume shaper configuration is generated with the provided duration and default
          * volume curve definitions. This config is then used to fade out players with given usage.
@@ -904,17 +928,18 @@
          * {@link #DURATION_NOT_SET} and sets the corresponding fade out volume shaper config to
          * {@code null}
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+         * @param usage the {@link android.media.AudioAttributes usage} of target player
          * @param fadeOutDurationMillis positive duration in milliseconds or
-         * {@link #DURATION_NOT_SET}
+         *     {@link #DURATION_NOT_SET}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the fade out duration is non-positive with the
-         * exception of {@link #DURATION_NOT_SET}
+         *     exception of {@link #DURATION_NOT_SET}
          * @see #setFadeOutVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)
          * @see #getFadeOutDurationForUsage(int)
          */
         @NonNull
-        public Builder setFadeOutDurationForUsage(int usage,  long fadeOutDurationMillis) {
+        public Builder setFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage,
+                @DurationMillisLong long fadeOutDurationMillis) {
             validateUsage(usage);
             VolumeShaper.Configuration fadeOutVShaperConfig =
                     createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -924,7 +949,7 @@
 
         /**
          * Set the duration used for fading in players with
-         * {@link android.media.AudioAttributes.AttributeUsage}
+         * {@link android.media.AudioAttributes usage}
          * <p>
          * A Volume shaper configuration is generated with the provided duration and default
          * volume curve definitions. This config is then used to fade in players with given usage.
@@ -934,17 +959,18 @@
          * {@link #DURATION_NOT_SET} and sets the corresponding fade in volume shaper config to
          * {@code null}
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage} of target player
+         * @param usage the {@link android.media.AudioAttributes usage} of target player
          * @param fadeInDurationMillis positive duration in milliseconds or
-         * {@link #DURATION_NOT_SET}
+         *     {@link #DURATION_NOT_SET}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the fade in duration is non-positive with the
-         * exception of {@link #DURATION_NOT_SET}
+         *     exception of {@link #DURATION_NOT_SET}
          * @see #setFadeInVolumeShaperConfigForUsage(int, VolumeShaper.Configuration)
          * @see #getFadeInDurationForUsage(int)
          */
         @NonNull
-        public Builder setFadeInDurationForUsage(int usage,  long fadeInDurationMillis) {
+        public Builder setFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage,
+                @DurationMillisLong long fadeInDurationMillis) {
             validateUsage(usage);
             VolumeShaper.Configuration fadeInVShaperConfig =
                     createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -962,9 +988,8 @@
          *
          * @param audioAttributes the {@link android.media.AudioAttributes}
          * @param fadeOutVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to
-         *                             fade out players with audio attribute
+         *     fade out players with audio attribute
          * @return the same Builder instance
-         * @throws NullPointerException if the audio attributes is {@code null}
          * @see #getFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes)
          */
         @NonNull
@@ -988,7 +1013,7 @@
          *
          * @param audioAttributes the {@link android.media.AudioAttributes}
          * @param fadeInVShaperConfig the {@link android.media.VolumeShaper.Configuration} used to
-         *                            fade in players with audio attribute
+         *     fade in players with audio attribute
          * @return the same Builder instance
          * @throws NullPointerException if the audio attributes is {@code null}
          * @see #getFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes)
@@ -1017,12 +1042,12 @@
          * {@code null}
          *
          * @param audioAttributes the {@link android.media.AudioAttributes} for which the fade out
-         * duration will be set/updated/reset
+         *     duration will be set/updated/reset
          * @param fadeOutDurationMillis positive duration in milliseconds or
-         * {@link #DURATION_NOT_SET}
+         *     {@link #DURATION_NOT_SET}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the fade out duration is non-positive with the
-         * exception of {@link #DURATION_NOT_SET}
+         *     exception of {@link #DURATION_NOT_SET}
          * @see #getFadeOutDurationForAudioAttributes(AudioAttributes)
          * @see #setFadeOutVolumeShaperConfigForAudioAttributes(AudioAttributes,
          * VolumeShaper.Configuration)
@@ -1030,7 +1055,7 @@
         @NonNull
         public Builder setFadeOutDurationForAudioAttributes(
                 @NonNull AudioAttributes audioAttributes,
-                long fadeOutDurationMillis) {
+                @DurationMillisLong long fadeOutDurationMillis) {
             Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
             VolumeShaper.Configuration fadeOutVShaperConfig =
                     createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -1039,8 +1064,7 @@
         }
 
         /**
-         * Set the duration used for fading in players of type
-         * {@link android.media.AudioAttributes}.
+         * Set the duration used for fading in players of type {@link android.media.AudioAttributes}
          * <p>
          * A Volume shaper configuration is generated with the provided duration and default
          * volume curve definitions. This config is then used to fade in players with given usage.
@@ -1051,19 +1075,19 @@
          * {@code null}
          *
          * @param audioAttributes the {@link android.media.AudioAttributes} for which the fade in
-         * duration will be set/updated/reset
+         *     duration will be set/updated/reset
          * @param fadeInDurationMillis positive duration in milliseconds or
-         * {@link #DURATION_NOT_SET}
+         *     {@link #DURATION_NOT_SET}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the fade in duration is non-positive with the
-         * exception of {@link #DURATION_NOT_SET}
+         *     exception of {@link #DURATION_NOT_SET}
          * @see #getFadeInDurationForAudioAttributes(AudioAttributes)
          * @see #setFadeInVolumeShaperConfigForAudioAttributes(AudioAttributes,
          * VolumeShaper.Configuration)
          */
         @NonNull
         public Builder setFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes,
-                long fadeInDurationMillis) {
+                @DurationMillisLong long fadeInDurationMillis) {
             Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
             VolumeShaper.Configuration fadeInVShaperConfig =
                     createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1072,22 +1096,18 @@
         }
 
         /**
-         * Set the list of {@link android.media.AudioAttributes.AttributeUsage} that can be faded
+         * Set the list of {@link android.media.AudioAttributes usage} that can be faded
          *
          * <p>This is a positive list. Players with matching usage will be considered for fading.
          * Usages that are not part of this list will not be faded
          *
-         * <p>Passing an empty list as input clears the existing list. This can be used to
-         * reset the list when using a copy constructor
-         *
          * <p><b>Warning:</b> When fade state is set to enabled, the builder expects at least one
          * usage to be set/added. Failure to do so will result in an exception during
          * {@link #build()}
          *
-         * @param usages List of the {@link android.media.AudioAttributes.AttributeUsage}
+         * @param usages List of the {@link android.media.AudioAttributes usages}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the usages are invalid
-         * @throws NullPointerException if the usage list is {@code null}
          * @see #getFadeableUsages()
          */
         @NonNull
@@ -1101,9 +1121,9 @@
         }
 
         /**
-         * Add the {@link android.media.AudioAttributes.AttributeUsage} to the fadeable list
+         * Add the {@link android.media.AudioAttributes usage} to the fadeable list
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+         * @param usage the {@link android.media.AudioAttributes usage}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the usage is invalid
          * @see #getFadeableUsages()
@@ -1120,30 +1140,23 @@
         }
 
         /**
-         * Remove the {@link android.media.AudioAttributes.AttributeUsage} from the fadeable list
-         * <p>
-         * Players of this usage type will not be faded.
+         * Clears the fadeable {@link android.media.AudioAttributes usage} list
          *
-         * @param usage the {@link android.media.AudioAttributes.AttributeUsage}
+         * <p>This can be used to reset the list when using a copy constructor
+         *
          * @return the same Builder instance
-         * @throws IllegalArgumentException if the usage is invalid
          * @see #getFadeableUsages()
          * @see #setFadeableUsages(List)
          */
         @NonNull
-        public Builder clearFadeableUsage(@AudioAttributes.AttributeUsage int usage) {
-            validateUsage(usage);
+        public Builder clearFadeableUsages() {
             setFlag(IS_FADEABLE_USAGES_FIELD_SET);
-            int index = mFadeableUsages.indexOf(usage);
-            if (index != INVALID_INDEX) {
-                mFadeableUsages.remove(index);
-            }
+            mFadeableUsages.clear();
             return this;
         }
 
         /**
-         * Set the list of {@link android.media.AudioAttributes.AttributeContentType} that can not
-         * be faded
+         * Set the list of {@link android.media.AudioAttributes content type} that can not be faded
          *
          * <p>This is a negative list. Players with matching content type of this list will not be
          * faded. Content types that are not part of this list will be considered for fading.
@@ -1151,10 +1164,9 @@
          * <p>Passing an empty list as input clears the existing list. This can be used to
          * reset the list when using a copy constructor
          *
-         * @param contentTypes list of {@link android.media.AudioAttributes.AttributeContentType}
+         * @param contentTypes list of {@link android.media.AudioAttributes content types}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the content types are invalid
-         * @throws NullPointerException if the content type list is {@code null}
          * @see #getUnfadeableContentTypes()
          */
         @NonNull
@@ -1168,9 +1180,9 @@
         }
 
         /**
-         * Add the {@link android.media.AudioAttributes.AttributeContentType} to unfadeable list
+         * Add the {@link android.media.AudioAttributes content type} to unfadeable list
          *
-         * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+         * @param contentType the {@link android.media.AudioAttributes content type}
          * @return the same Builder instance
          * @throws IllegalArgumentException if the content type is invalid
          * @see #setUnfadeableContentTypes(List)
@@ -1188,24 +1200,18 @@
         }
 
         /**
-         * Remove the {@link android.media.AudioAttributes.AttributeContentType} from the
-         * unfadeable list
+         * Clears the unfadeable {@link android.media.AudioAttributes content type} list
          *
-         * @param contentType the {@link android.media.AudioAttributes.AttributeContentType}
+         * <p>This can be used to reset the list when using a copy constructor
+         *
          * @return the same Builder instance
-         * @throws IllegalArgumentException if the content type is invalid
          * @see #setUnfadeableContentTypes(List)
          * @see #getUnfadeableContentTypes()
          */
         @NonNull
-        public Builder clearUnfadeableContentType(
-                @AudioAttributes.AttributeContentType int contentType) {
-            validateContentType(contentType);
+        public Builder clearUnfadeableContentTypes() {
             setFlag(IS_UNFADEABLE_CONTENT_TYPE_FIELD_SET);
-            int index = mUnfadeableContentTypes.indexOf(contentType);
-            if (index != INVALID_INDEX) {
-                mUnfadeableContentTypes.remove(index);
-            }
+            mUnfadeableContentTypes.clear();
             return this;
         }
 
@@ -1213,14 +1219,10 @@
          * Set the uids that cannot be faded
          *
          * <p>This is a negative list. Players with matching uid of this list will not be faded.
-         * Uids that are not part of this list shall be considered for fading
-         *
-         * <p>Passing an empty list as input clears the existing list. This can be used to
-         * reset the list when using a copy constructor
+         * Uids that are not part of this list shall be considered for fading.
          *
          * @param uids list of uids
          * @return the same Builder instance
-         * @throws NullPointerException if the uid list is {@code null}
          * @see #getUnfadeableUids()
          */
         @NonNull
@@ -1248,19 +1250,17 @@
         }
 
         /**
-         * Remove the uid from unfadeable list
+         * Clears the unfadeable uid list
          *
-         * @param uid client uid
+         * <p>This can be used to reset the list when using a copy constructor.
+         *
          * @return the same Builder instance
          * @see #setUnfadeableUids(List)
          * @see #getUnfadeableUids()
          */
         @NonNull
-        public Builder clearUnfadeableUid(int uid) {
-            int index = mUnfadeableUids.indexOf(uid);
-            if (index != INVALID_INDEX) {
-                mUnfadeableUids.remove(index);
-            }
+        public Builder clearUnfadeableUids() {
+            mUnfadeableUids.clear();
             return this;
         }
 
@@ -1270,24 +1270,19 @@
          * <p>This is a negative list. Players with matching audio attributes of this list will not
          * be faded. Audio attributes that are not part of this list shall be considered for fading.
          *
-         * <p>Passing an empty list as input clears any existing list. This can be used to
-         * reset the list when using a copy constructor
-         *
          * <p><b>Note:</b> Be cautious when adding generic audio attributes into this list as it can
-         * negatively impact fadeability decision if such an audio attribute and corresponding
-         * usage fall into opposing lists.
+         * negatively impact fadeability decision (if such an audio attribute and corresponding
+         * usage fall into opposing lists).
          * For example:
          * <pre class=prettyprint>
          *    AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build() </pre>
          * is a generic audio attribute for {@link android.media.AudioAttributes.USAGE_MEDIA}.
-         * It is an undefined behavior to have an
-         * {@link android.media.AudioAttributes.AttributeUsage} in the fadeable usage list and the
-         * corresponding generic {@link android.media.AudioAttributes} in the unfadeable list. Such
-         * cases will result in an exception during {@link #build()}
+         * It is an undefined behavior to have an {@link android.media.AudioAttributes usage} in the
+         * fadeable usage list and the corresponding generic {@link android.media.AudioAttributes}
+         * in the unfadeable list. Such cases will result in an exception during {@link #build()}.
          *
          * @param attrs list of {@link android.media.AudioAttributes}
          * @return the same Builder instance
-         * @throws NullPointerException if the audio attributes list is {@code null}
          * @see #getUnfadeableAudioAttributes()
          */
         @NonNull
@@ -1303,7 +1298,6 @@
          *
          * @param audioAttributes the {@link android.media.AudioAttributes}
          * @return the same Builder instance
-         * @throws NullPointerException if the audio attributes is {@code null}
          * @see #setUnfadeableAudioAttributes(List)
          * @see #getUnfadeableAudioAttributes()
          */
@@ -1317,19 +1311,16 @@
         }
 
         /**
-         * Remove the {@link android.media.AudioAttributes} from the unfadeable list.
+         * Clears the unfadeable {@link android.media.AudioAttributes} list.
          *
-         * @param audioAttributes the {@link android.media.AudioAttributes}
+         * <p>This can be used to reset the list when using a copy constructor.
+         *
          * @return the same Builder instance
-         * @throws NullPointerException if the audio attributes is {@code null}
          * @see #getUnfadeableAudioAttributes()
          */
         @NonNull
-        public Builder clearUnfadeableAudioAttributes(@NonNull AudioAttributes audioAttributes) {
-            Objects.requireNonNull(audioAttributes, "Audio attributes cannot be null");
-            if (mUnfadeableAudioAttributes.contains(audioAttributes)) {
-                mUnfadeableAudioAttributes.remove(audioAttributes);
-            }
+        public Builder clearUnfadeableAudioAttributes() {
+            mUnfadeableAudioAttributes.clear();
             return this;
         }
 
@@ -1345,7 +1336,7 @@
          * @see #getFadeInDelayForOffenders()
          */
         @NonNull
-        public Builder setFadeInDelayForOffenders(long delayMillis) {
+        public Builder setFadeInDelayForOffenders(@DurationMillisLong long delayMillis) {
             Preconditions.checkArgument(delayMillis >= 0, "Delay cannot be negative");
             mFadeInDelayForOffendersMillis = delayMillis;
             return this;
@@ -1469,7 +1460,6 @@
             switch(state) {
                 case FADE_STATE_DISABLED:
                 case FADE_STATE_ENABLED_DEFAULT:
-                case FADE_STATE_ENABLED_AUTO:
                     break;
                 default:
                     throw new IllegalArgumentException("Unknown fade state: " + state);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8dfa6be..98bd3ca 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -365,6 +365,8 @@
 
     oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
 
+    List<AudioMix> getRegisteredPolicyMixes();
+
     void unregisterAudioPolicy(in IAudioPolicyCallback pcb);
 
     int addMixForPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb);
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index b85decc..bbe461c 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -58,6 +58,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -597,6 +598,21 @@
         setRegistration(null);
     }
 
+    /**
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @FlaggedApi(Flags.FLAG_AUDIO_MIX_TEST_API)
+    public List<AudioMix> getMixes() {
+        if (!Flags.audioMixTestApi()) {
+            return Collections.emptyList();
+        }
+        synchronized (mLock) {
+            return List.copyOf(mConfig.getMixes());
+        }
+    }
+
     public void setRegistration(String regId) {
         synchronized (mLock) {
             mRegistrationId = regId;
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
index f105ae9..236b1fd 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/FadeManagerConfigurationUnitTest.java
@@ -45,8 +45,10 @@
 @RunWith(AndroidJUnit4.class)
 @RequiresFlagsEnabled(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION)
 public final class FadeManagerConfigurationUnitTest {
-    private static final long DEFAULT_FADE_OUT_DURATION_MS = 2_000;
-    private static final long DEFAULT_FADE_IN_DURATION_MS = 1_000;
+    private static final long DEFAULT_FADE_OUT_DURATION_MS =
+            FadeManagerConfiguration.getDefaultFadeOutDurationMillis();
+    private static final long DEFAULT_FADE_IN_DURATION_MS =
+            FadeManagerConfiguration.getDefaultFadeInDurationMillis();
     private static final long TEST_FADE_OUT_DURATION_MS = 1_500;
     private static final long TEST_FADE_IN_DURATION_MS = 750;
     private static final int TEST_INVALID_USAGE = -10;
@@ -259,16 +261,6 @@
     }
 
     @Test
-    public void testSetFadeState_toEnableAuto() {
-        final int fadeStateAuto = FadeManagerConfiguration.FADE_STATE_ENABLED_AUTO;
-        FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder()
-                .setFadeState(fadeStateAuto).build();
-
-        expect.withMessage("Fade state when enabled for audio").that(fmc.getFadeState())
-                .isEqualTo(fadeStateAuto);
-    }
-
-    @Test
     public void testSetFadeState_toInvalid_fails() {
         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
                 new FadeManagerConfiguration.Builder()
@@ -310,13 +302,13 @@
     }
 
     @Test
-    public void testSetFadeVolShaperConfig_withNullVolumeShaper_getsNull() {
+    public void testSetFadeOutVolShaperConfig_withNullVolumeShaper_getsNull() {
         FadeManagerConfiguration fmc = new FadeManagerConfiguration.Builder(mFmc)
                 .setFadeOutVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE,
                         /* VolumeShaper.Configuration= */ null)
                 .setFadeInVolumeShaperConfigForAudioAttributes(TEST_MEDIA_AUDIO_ATTRIBUTE,
                         /* VolumeShaper.Configuration= */ null)
-                .clearFadeableUsage(AudioAttributes.USAGE_MEDIA).build();
+                .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_MEDIA).build();
 
         expect.withMessage("Fade out volume shaper config set with null value")
                 .that(fmc.getFadeOutVolumeShaperConfigForAudioAttributes(
@@ -547,31 +539,13 @@
     }
 
     @Test
-    public void testClearFadeableUsage() {
-        final int usageToClear = AudioAttributes.USAGE_MEDIA;
-        List<Integer> updatedUsages = new ArrayList<>(mFmc.getFadeableUsages());
-        updatedUsages.remove((Integer) usageToClear);
+    public void testClearFadeableUsages() {
+        FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(mFmc)
+                .clearFadeableUsages().addFadeableUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+                .build();
 
-        FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration
-                .Builder(mFmc).clearFadeableUsage(usageToClear).build();
-
-        expect.withMessage("Clear fadeable usage").that(updatedFmc.getFadeableUsages())
-                .containsExactlyElementsIn(updatedUsages);
-    }
-
-    @Test
-    public void testClearFadeableUsage_withInvalidUsage_fails() {
-        FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc);
-
-        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
-                fmcBuilder.clearFadeableUsage(TEST_INVALID_USAGE)
-        );
-
-        FadeManagerConfiguration fmc = fmcBuilder.build();
-        expect.withMessage("Clear invalid usage").that(thrown).hasMessageThat()
-                .contains("Invalid usage");
-        expect.withMessage("Fadeable usages").that(fmc.getFadeableUsages())
-                .containsExactlyElementsIn(mFmc.getFadeableUsages());
+        expect.withMessage("Clear fadeable usages").that(updatedFmc.getFadeableUsages())
+                .containsExactlyElementsIn(List.of(AudioAttributes.USAGE_VOICE_COMMUNICATION));
     }
 
     @Test
@@ -673,7 +647,7 @@
     }
 
     @Test
-    public void testClearUnfadeableContentType() {
+    public void testClearUnfadeableContentTypes() {
         List<Integer> unfadeableContentTypes = new ArrayList<>(Arrays.asList(
                 AudioAttributes.CONTENT_TYPE_MOVIE,
                 AudioAttributes.CONTENT_TYPE_SONIFICATION
@@ -682,23 +656,10 @@
 
         FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder()
                 .setUnfadeableContentTypes(unfadeableContentTypes)
-                .clearUnfadeableContentType(contentTypeToClear).build();
+                .clearUnfadeableContentTypes().build();
 
-        unfadeableContentTypes.remove((Integer) contentTypeToClear);
         expect.withMessage("Unfadeable content types").that(updatedFmc.getUnfadeableContentTypes())
-                .containsExactlyElementsIn(unfadeableContentTypes);
-    }
-
-    @Test
-    public void testClearUnfadeableContentType_withInvalidContentType_fails() {
-        FadeManagerConfiguration.Builder fmcBuilder = new FadeManagerConfiguration.Builder(mFmc);
-
-        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
-                fmcBuilder.clearUnfadeableContentType(TEST_INVALID_CONTENT_TYPE).build()
-        );
-
-        expect.withMessage("Invalid content type exception").that(thrown).hasMessageThat()
-                .contains("Invalid content type");
+                .isEmpty();
     }
 
     @Test
@@ -735,7 +696,7 @@
     }
 
     @Test
-    public void testClearUnfadebaleUid() {
+    public void testClearUnfadebaleUids() {
         final List<Integer> unfadeableUids = List.of(
                 TEST_UID_1,
                 TEST_UID_2
@@ -744,10 +705,9 @@
                 .setUnfadeableUids(unfadeableUids).build();
 
         FadeManagerConfiguration updatedFmc = new FadeManagerConfiguration.Builder(fmc)
-                .clearUnfadeableUid(TEST_UID_1).build();
+                .clearUnfadeableUids().build();
 
-        expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids())
-                .isEqualTo(List.of(TEST_UID_2));
+        expect.withMessage("Unfadeable uids").that(updatedFmc.getUnfadeableUids()).isEmpty();
     }
 
     @Test
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 9a2cf61..e7d1072 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -40,7 +40,7 @@
             Log.d(TAG, "Received UI cancel request, shouldShowCancellationUi: $this")
         }
         if (showCancel) {
-            val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+            val appLabel = packageManager.appLabel(cancelUiRequest.packageName)
             if (appLabel == null) {
                 Log.d(TAG, "Received UI cancel request with an invalid package name.")
                 null
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index b13df61..3097387 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -119,7 +119,7 @@
 
         val cancellationRequest = getCancelUiRequest(intent)
         val cancelUiRequestState = cancellationRequest?.let {
-            CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
+            CancelUiRequestState(getAppLabel(context.getPackageManager(), it.packageName))
         }
 
         initialUiState = when (requestInfo?.type) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 26a97cd..4771237 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -135,7 +135,7 @@
         Log.d(
             Constants.LOG_TAG, "Received UI cancellation intent. Should show cancellation" +
             " ui = $shouldShowCancellationUi")
-        val appDisplayName = getAppLabel(packageManager, cancelUiRequest.appPackageName)
+        val appDisplayName = getAppLabel(packageManager, cancelUiRequest.packageName)
         if (!shouldShowCancellationUi) {
             this.finish()
         }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 6c5a984..f4da1e6 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -72,7 +72,7 @@
 
     init {
         uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INIT,
-            credManRepo.requestInfo?.appPackageName)
+            credManRepo.requestInfo?.packageName)
     }
 
     /**************************************************************************/
@@ -107,7 +107,7 @@
         if (this.credManRepo.requestInfo?.token != credManRepo.requestInfo?.token) {
             this.uiMetrics.resetInstanceId()
             this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_NEW_REQUEST,
-                credManRepo.requestInfo?.appPackageName)
+                credManRepo.requestInfo?.packageName)
         }
     }
 
@@ -189,7 +189,7 @@
     private fun onInternalError() {
         Log.w(Constants.LOG_TAG, "UI closed due to illegal internal state")
         this.uiMetrics.logNormal(LifecycleEvent.CREDMAN_ACTIVITY_INTERNAL_ERROR,
-            credManRepo.requestInfo?.appPackageName)
+            credManRepo.requestInfo?.packageName)
         credManRepo.onParsingFailureCancel()
         uiState = uiState.copy(dialogState = DialogState.COMPLETE)
     }
@@ -399,6 +399,6 @@
 
     @Composable
     fun logUiEvent(uiEventEnum: UiEventEnum) {
-        this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.appPackageName)
+        this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName)
     }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 64595e2..997c45e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -195,7 +195,7 @@
                 }
             return com.android.credentialmanager.getflow.RequestDisplayInfo(
                 appName = originName?.ifEmpty { null }
-                    ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
+                    ?: getAppLabel(context.packageManager, requestInfo.packageName)
                     ?: return null,
                 preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
                 preferIdentityDocUi = getCredentialRequest.data.getBoolean(
@@ -269,7 +269,7 @@
                 return null
             }
             val appLabel = originName?.ifEmpty { null }
-                ?: getAppLabel(context.packageManager, requestInfo.appPackageName)
+                ?: getAppLabel(context.packageManager, requestInfo.packageName)
                 ?: return null
             val createCredentialRequest = requestInfo.createCredentialRequest ?: return null
             val createCredentialRequestJetpack = CreateCredentialRequest.createFrom(
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index f44b161..aed985e 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -14,10 +14,10 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
            parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:textColor">@color/settingslib_text_color_primary</item>
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
         <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
new file mode 100644
index 0000000..2a4658b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/SpatializerRepository.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.settingslib.media.data.repository
+
+import android.media.AudioDeviceAttributes
+import android.media.Spatializer
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+interface SpatializerRepository {
+
+    /**
+     * Returns true when Spatial audio feature is supported for the [audioDeviceAttributes] and
+     * false the otherwise.
+     */
+    suspend fun isAvailableForDevice(audioDeviceAttributes: AudioDeviceAttributes): Boolean
+
+    /** Returns a list [AudioDeviceAttributes] that are compatible with spatial audio. */
+    suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes>
+
+    /** Adds a [audioDeviceAttributes] to [getCompatibleDevices] list. */
+    suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes)
+
+    /** Removes a [audioDeviceAttributes] to [getCompatibleDevices] list. */
+    suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes)
+}
+
+class SpatializerRepositoryImpl(
+    private val spatializer: Spatializer,
+    private val backgroundContext: CoroutineContext,
+) : SpatializerRepository {
+
+    override suspend fun isAvailableForDevice(
+        audioDeviceAttributes: AudioDeviceAttributes
+    ): Boolean {
+        return withContext(backgroundContext) {
+            spatializer.isAvailableForDevice(audioDeviceAttributes)
+        }
+    }
+
+    override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> =
+        withContext(backgroundContext) { spatializer.compatibleAudioDevices }
+
+    override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+        withContext(backgroundContext) {
+            spatializer.addCompatibleAudioDevice(audioDeviceAttributes)
+        }
+    }
+
+    override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+        withContext(backgroundContext) {
+            spatializer.removeCompatibleAudioDevice(audioDeviceAttributes)
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
new file mode 100644
index 0000000..c3cc340
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/domain/interactor/SpatializerInteractor.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import com.android.settingslib.media.data.repository.SpatializerRepository
+
+class SpatializerInteractor(private val repository: SpatializerRepository) {
+
+    suspend fun isAvailable(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
+        repository.isAvailableForDevice(audioDeviceAttributes)
+
+    /** Checks if spatial audio is enabled for the [audioDeviceAttributes]. */
+    suspend fun isEnabled(audioDeviceAttributes: AudioDeviceAttributes): Boolean =
+        repository.getCompatibleDevices().contains(audioDeviceAttributes)
+
+    /** Enblaes or disables spatial audio for [audioDeviceAttributes]. */
+    suspend fun setEnabled(audioDeviceAttributes: AudioDeviceAttributes, isEnabled: Boolean) {
+        if (isEnabled) {
+            repository.addCompatibleDevice(audioDeviceAttributes)
+        } else {
+            repository.removeCompatibleDevice(audioDeviceAttributes)
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt
new file mode 100644
index 0000000..3f52f24
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/FakeSpatializerRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import com.android.settingslib.media.data.repository.SpatializerRepository
+
+class FakeSpatializerRepository : SpatializerRepository {
+
+    private val availabilityByDevice: MutableMap<AudioDeviceAttributes, Boolean> = mutableMapOf()
+    private val compatibleDevices: MutableList<AudioDeviceAttributes> = mutableListOf()
+
+    override suspend fun isAvailableForDevice(
+        audioDeviceAttributes: AudioDeviceAttributes
+    ): Boolean = availabilityByDevice.getOrDefault(audioDeviceAttributes, false)
+
+    override suspend fun getCompatibleDevices(): Collection<AudioDeviceAttributes> =
+        compatibleDevices
+
+    override suspend fun addCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+        compatibleDevices.add(audioDeviceAttributes)
+    }
+
+    override suspend fun removeCompatibleDevice(audioDeviceAttributes: AudioDeviceAttributes) {
+        compatibleDevices.remove(audioDeviceAttributes)
+    }
+
+    fun setIsAvailable(audioDeviceAttributes: AudioDeviceAttributes, isAvailable: Boolean) {
+        availabilityByDevice[audioDeviceAttributes] = isAvailable
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt
new file mode 100644
index 0000000..a44baeb
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/domain/interactor/SpatializerInteractorTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.android.settingslib.media.domain.interactor
+
+import android.media.AudioDeviceAttributes
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SpatializerInteractorTest {
+
+    private val testScope = TestScope()
+    private val underTest = SpatializerInteractor(FakeSpatializerRepository())
+
+    @Test
+    fun setEnabledFalse_isEnabled_false() {
+        testScope.runTest {
+            underTest.setEnabled(deviceAttributes, false)
+
+            assertThat(underTest.isEnabled(deviceAttributes)).isFalse()
+        }
+    }
+
+    @Test
+    fun setEnabledTrue_isEnabled_true() {
+        testScope.runTest {
+            underTest.setEnabled(deviceAttributes, true)
+
+            assertThat(underTest.isEnabled(deviceAttributes)).isTrue()
+        }
+    }
+
+    private companion object {
+        val deviceAttributes = AudioDeviceAttributes(0, 0, "test_device")
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8ad5f24..dc8116d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -261,6 +261,7 @@
         Settings.Secure.CREDENTIAL_SERVICE,
         Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
         Settings.Secure.EVEN_DIMMER_ACTIVATED,
-        Settings.Secure.EVEN_DIMMER_MIN_NITS
+        Settings.Secure.EVEN_DIMMER_MIN_NITS,
+        Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d854df38..fabdafc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -416,5 +416,6 @@
         VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR);
         VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR);
         VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
+        VALIDATORS.put(Secure.STYLUS_POINTER_ICON_ENABLED, BOOLEAN_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e4a762a..bc07836 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2601,6 +2601,9 @@
         p.end(soundsToken);
 
         dumpSetting(s, p,
+                Settings.Secure.STYLUS_POINTER_ICON_ENABLED,
+                SecureSettingsProto.STYLUS_POINTER_ICON_ENABLED);
+        dumpSetting(s, p,
                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
                 SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
         dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e99fcc9..84ef6e5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -914,6 +914,9 @@
     <!-- Permission required for Cts test ScreenRecordingCallbackTests -->
     <uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
 
+    <!-- Permissions required for CTS test - GrammaticalInflectionManagerTest -->
+    <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f877d7a..12e8f57 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -262,6 +262,9 @@
 
     <uses-permission android:name="android.permission.MODIFY_THEME_OVERLAY" />
 
+    <!-- Activity Manager -->
+    <uses-permission android:name="android.permission.SET_THEME_OVERLAY_CONTROLLER_READY" />
+
     <!-- accessibility -->
     <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
     <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
similarity index 85%
rename from packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 4973caf..c489795 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -45,13 +45,13 @@
 import com.android.internal.policy.ScreenDecorationsUtils
 import kotlin.math.roundToInt
 
-private const val TAG = "ActivityLaunchAnimator"
+private const val TAG = "ActivityTransitionAnimator"
 
 /**
  * A class that allows activities to be started in a seamless way from a view that is transforming
  * nicely into the starting window.
  */
-class ActivityLaunchAnimator(
+class ActivityTransitionAnimator(
     /** The animator used when animating a View into an app. */
     private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
 
@@ -97,7 +97,7 @@
             )
 
         // TODO(b/288507023): Remove this flag.
-        @JvmField val DEBUG_LAUNCH_ANIMATION = Build.IS_DEBUGGABLE
+        @JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE
 
         private val DEFAULT_TRANSITION_ANIMATOR = TransitionAnimator(TIMINGS, INTERPOLATORS)
         private val DEFAULT_DIALOG_TO_APP_ANIMATOR =
@@ -113,13 +113,13 @@
         private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
 
         /** The time we wait before timing out the remote animation after starting the intent. */
-        private const val LAUNCH_TIMEOUT = 1_000L
+        private const val TRANSITION_TIMEOUT = 1_000L
 
         /**
          * The time we wait before we Log.wtf because the remote animation was neither started or
          * cancelled by WM.
          */
-        private const val LONG_LAUNCH_TIMEOUT = 5_000L
+        private const val LONG_TRANSITION_TIMEOUT = 5_000L
     }
 
     /**
@@ -134,20 +134,20 @@
     /** Top-level listener that can be used to notify all registered [listeners]. */
     private val lifecycleListener =
         object : Listener {
-            override fun onLaunchAnimationStart() {
-                listeners.forEach { it.onLaunchAnimationStart() }
+            override fun onTransitionAnimationStart() {
+                listeners.forEach { it.onTransitionAnimationStart() }
             }
 
-            override fun onLaunchAnimationEnd() {
-                listeners.forEach { it.onLaunchAnimationEnd() }
+            override fun onTransitionAnimationEnd() {
+                listeners.forEach { it.onTransitionAnimationEnd() }
             }
 
-            override fun onLaunchAnimationProgress(linearProgress: Float) {
-                listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
+            override fun onTransitionAnimationProgress(linearProgress: Float) {
+                listeners.forEach { it.onTransitionAnimationProgress(linearProgress) }
             }
 
-            override fun onLaunchAnimationCancelled() {
-                listeners.forEach { it.onLaunchAnimationCancelled() }
+            override fun onTransitionAnimationCancelled() {
+                listeners.forEach { it.onTransitionAnimationCancelled() }
             }
         }
 
@@ -188,7 +188,7 @@
         val callback =
             this.callback
                 ?: throw IllegalStateException(
-                    "ActivityLaunchAnimator.callback must be set before using this animator"
+                    "ActivityTransitionAnimator.callback must be set before using this animator"
                 )
         val runner = createRunner(controller)
         val runnerDelegate = runner.delegate!!
@@ -260,7 +260,7 @@
                 callOnIntentStartedOnMainThread(willAnimate)
             }
         } else {
-            if (DEBUG_LAUNCH_ANIMATION) {
+            if (DEBUG_TRANSITION_ANIMATION) {
                 Log.d(
                     TAG,
                     "Calling controller.onIntentStarted(willAnimate=$willAnimate) " +
@@ -293,7 +293,7 @@
         }
     }
 
-    /** Add a [Listener] that can listen to launch animations. */
+    /** Add a [Listener] that can listen to transition animations. */
     fun addListener(listener: Listener) {
         listeners.add(listener)
     }
@@ -340,24 +340,24 @@
     }
 
     interface Listener {
-        /** Called when an activity launch animation started. */
-        fun onLaunchAnimationStart() {}
+        /** Called when an activity transition animation started. */
+        fun onTransitionAnimationStart() {}
 
         /**
-         * Called when an activity launch animation is finished. This will be called if and only if
-         * [onLaunchAnimationStart] was called earlier.
+         * Called when an activity transition animation is finished. This will be called if and only
+         * if [onTransitionAnimationStart] was called earlier.
          */
-        fun onLaunchAnimationEnd() {}
+        fun onTransitionAnimationEnd() {}
 
         /**
-         * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
-         * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
-         * before the cancellation.
+         * The animation was cancelled. Note that [onTransitionAnimationEnd] will still be called
+         * after this if the animation was already started, i.e. if [onTransitionAnimationStart] was
+         * called before the cancellation.
          */
-        fun onLaunchAnimationCancelled() {}
+        fun onTransitionAnimationCancelled() {}
 
-        /** Called when an activity launch animation made progress. */
-        fun onLaunchAnimationProgress(linearProgress: Float) {}
+        /** Called when an activity transition animation made progress. */
+        fun onTransitionAnimationProgress(linearProgress: Float) {}
     }
 
     /**
@@ -383,9 +383,10 @@
                 // issues.
                 if (view !is LaunchableView) {
                     throw IllegalArgumentException(
-                        "An ActivityLaunchAnimator.Controller was created from a View that does " +
-                            "not implement LaunchableView. This can lead to subtle bugs where the" +
-                            " visibility of the View we are launching from is not what we expected."
+                        "An ActivityTransitionAnimator.Controller was created from a View that " +
+                            "does not implement LaunchableView. This can lead to subtle bugs " +
+                            "where the visibility of the View we are launching from is not what " +
+                            "we expected."
                     )
                 }
 
@@ -411,11 +412,11 @@
             get() = false
 
         /**
-         * Whether the expandable controller by this [Controller] is below the launching window that
-         * is going to be animated.
+         * Whether the expandable controller by this [Controller] is below the window that is going
+         * to be animated.
          *
-         * This should be `false` when launching an app from the shade or status bar, given that
-         * they are drawn above all apps. This is usually `true` when using this launcher in a
+         * This should be `false` when animating an app from or to the shade or status bar, given
+         * that they are drawn above all apps. This is usually `true` when using this animator in a
          * normal app or a launcher, that are drawn below the animating activity/window.
          */
         val isBelowAnimatingWindow: Boolean
@@ -432,10 +433,11 @@
          * after this if the animation was already started, i.e. if [onTransitionAnimationStart] was
          * called before the cancellation.
          *
-         * If this launch animation affected the occlusion state of the keyguard, WM will provide us
-         * with [newKeyguardOccludedState] so that we can set the occluded state appropriately.
+         * If this transition animation affected the occlusion state of the keyguard, WM will
+         * provide us with [newKeyguardOccludedState] so that we can set the occluded state
+         * appropriately.
          */
-        fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
+        fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
     }
 
     /**
@@ -449,24 +451,24 @@
     ) : Listener {
         var cancelled = false
 
-        override fun onLaunchAnimationStart() {
-            delegate?.onLaunchAnimationStart()
+        override fun onTransitionAnimationStart() {
+            delegate?.onTransitionAnimationStart()
         }
 
-        override fun onLaunchAnimationProgress(linearProgress: Float) {
-            delegate?.onLaunchAnimationProgress(linearProgress)
+        override fun onTransitionAnimationProgress(linearProgress: Float) {
+            delegate?.onTransitionAnimationProgress(linearProgress)
         }
 
-        override fun onLaunchAnimationEnd() {
-            delegate?.onLaunchAnimationEnd()
+        override fun onTransitionAnimationEnd() {
+            delegate?.onTransitionAnimationEnd()
             if (!cancelled) {
                 onAnimationComplete.invoke()
             }
         }
 
-        override fun onLaunchAnimationCancelled() {
+        override fun onTransitionAnimationCancelled() {
             cancelled = true
-            delegate?.onLaunchAnimationCancelled()
+            delegate?.onTransitionAnimationCancelled()
             onAnimationComplete.invoke()
         }
     }
@@ -475,7 +477,7 @@
     inner class Runner(
         controller: Controller,
         callback: Callback,
-        /** The animator to use to animate the window launch. */
+        /** The animator to use to animate the window transition. */
         transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
         /** Listener for animation lifecycle events. */
         listener: Listener? = null
@@ -543,7 +545,7 @@
         private val callback: Callback,
         /** Listener for animation lifecycle events. */
         private val listener: Listener? = null,
-        /** The animator to use to animate the window launch. */
+        /** The animator to use to animate the window transition. */
         private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
 
         /**
@@ -574,8 +576,8 @@
         private var animation: TransitionAnimator.Animation? = null
 
         /**
-         * A timeout to cancel the launch animation if the remote animation is not started or
-         * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started.
+         * A timeout to cancel the transition animation if the remote animation is not started or
+         * cancelled within [TRANSITION_TIMEOUT] milliseconds after the intent was started.
          *
          * Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
          * it will be automatically converted when posted and we wouldn't be able to remove it after
@@ -585,21 +587,22 @@
 
         /**
          * A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't
-         * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was
+         * started or cancelled within [LONG_TRANSITION_TIMEOUT] milliseconds after the intent was
          * started.
          */
         private var onLongTimeout = Runnable {
             Log.wtf(
                 TAG,
-                "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT"
+                "The remote animation was neither cancelled or started within " +
+                    "$LONG_TRANSITION_TIMEOUT"
             )
         }
 
         @UiThread
         internal fun postTimeouts() {
             if (timeoutHandler != null) {
-                timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT)
-                timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT)
+                timeoutHandler.postDelayed(onTimeout, TRANSITION_TIMEOUT)
+                timeoutHandler.postDelayed(onLongTimeout, LONG_TRANSITION_TIMEOUT)
             }
         }
 
@@ -670,14 +673,14 @@
                 Log.i(TAG, "Aborting the animation as no window is opening")
                 iCallback?.invoke()
 
-                if (DEBUG_LAUNCH_ANIMATION) {
+                if (DEBUG_TRANSITION_ANIMATION) {
                     Log.d(
                         TAG,
-                        "Calling controller.onLaunchAnimationCancelled() [no window opening]"
+                        "Calling controller.onTransitionAnimationCancelled() [no window opening]"
                     )
                 }
-                controller.onLaunchAnimationCancelled()
-                listener?.onLaunchAnimationCancelled()
+                controller.onTransitionAnimationCancelled()
+                listener?.onTransitionAnimationCancelled()
                 return
             }
 
@@ -720,27 +723,29 @@
             val controller =
                 object : Controller by delegate {
                     override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
-                        listener?.onLaunchAnimationStart()
+                        listener?.onTransitionAnimationStart()
 
-                        if (DEBUG_LAUNCH_ANIMATION) {
+                        if (DEBUG_TRANSITION_ANIMATION) {
                             Log.d(
                                 TAG,
-                                "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
-                                    "$isExpandingFullyAbove) [controller=$delegate]"
+                                "Calling controller.onTransitionAnimationStart(" +
+                                    "isExpandingFullyAbove=$isExpandingFullyAbove) " +
+                                    "[controller=$delegate]"
                             )
                         }
                         delegate.onTransitionAnimationStart(isExpandingFullyAbove)
                     }
 
                     override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
-                        listener?.onLaunchAnimationEnd()
+                        listener?.onTransitionAnimationEnd()
                         iCallback?.invoke()
 
-                        if (DEBUG_LAUNCH_ANIMATION) {
+                        if (DEBUG_TRANSITION_ANIMATION) {
                             Log.d(
                                 TAG,
-                                "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
-                                    "$isExpandingFullyAbove) [controller=$delegate]"
+                                "Calling controller.onTransitionAnimationEnd(" +
+                                    "isExpandingFullyAbove=$isExpandingFullyAbove) " +
+                                    "[controller=$delegate]"
                             )
                         }
                         delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
@@ -758,7 +763,7 @@
                         }
                         navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
 
-                        listener?.onLaunchAnimationProgress(linearProgress)
+                        listener?.onTransitionAnimationProgress(linearProgress)
                         delegate.onTransitionAnimationProgress(state, progress, linearProgress)
                     }
                 }
@@ -904,7 +909,7 @@
         }
 
         private fun onAnimationTimedOut() {
-            // The remote animation was cancelled by WM, so we already cancelled the launch
+            // The remote animation was cancelled by WM, so we already cancelled the transition
             // animation.
             if (cancelled) {
                 return
@@ -913,18 +918,21 @@
             Log.w(TAG, "Remote animation timed out")
             timedOut = true
 
-            if (DEBUG_LAUNCH_ANIMATION) {
-                Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
+            if (DEBUG_TRANSITION_ANIMATION) {
+                Log.d(
+                    TAG,
+                    "Calling controller.onTransitionAnimationCancelled() [animation timed out]"
+                )
             }
-            controller.onLaunchAnimationCancelled()
-            listener?.onLaunchAnimationCancelled()
+            controller.onTransitionAnimationCancelled()
+            listener?.onTransitionAnimationCancelled()
         }
 
         @UiThread
         override fun onAnimationCancelled() {
             removeTimeouts()
 
-            // The short timeout happened, so we already cancelled the launch animation.
+            // The short timeout happened, so we already cancelled the transition animation.
             if (timedOut) {
                 return
             }
@@ -934,14 +942,15 @@
 
             animation?.cancel()
 
-            if (DEBUG_LAUNCH_ANIMATION) {
+            if (DEBUG_TRANSITION_ANIMATION) {
                 Log.d(
                     TAG,
-                    "Calling controller.onLaunchAnimationCancelled() [remote animation cancelled]",
+                    "Calling controller.onTransitionAnimationCancelled() [remote animation " +
+                        "cancelled]",
                 )
             }
-            controller.onLaunchAnimationCancelled()
-            listener?.onLaunchAnimationCancelled()
+            controller.onTransitionAnimationCancelled()
+            listener?.onTransitionAnimationCancelled()
         }
 
         private fun IRemoteAnimationFinishedCallback.invoke() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
index b879ba0..a53ab62 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -17,10 +17,10 @@
 package com.android.systemui.animation
 
 /**
- * A base class to easily create an implementation of [ActivityLaunchAnimator.Controller] which
+ * A base class to easily create an implementation of [ActivityTransitionAnimator.Controller] which
  * delegates most of its call to [delegate]. This is mostly useful for Java code which can't easily
  * create such a delegated class.
  */
 open class DelegateLaunchAnimatorController(
-    protected val delegate: ActivityLaunchAnimator.Controller
-) : ActivityLaunchAnimator.Controller by delegate
+    protected val delegate: ActivityTransitionAnimator.Controller
+) : ActivityTransitionAnimator.Controller by delegate
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9a36960..ed7f31c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -62,13 +62,14 @@
     private val isForTesting: Boolean = false,
 ) {
     private companion object {
-        private val TIMINGS = ActivityLaunchAnimator.TIMINGS
+        private val TIMINGS = ActivityTransitionAnimator.TIMINGS
 
         // We use the same interpolator for X and Y axis to make sure the dialog does not move out
         // of the screen bounds during the animation.
         private val INTERPOLATORS =
-            ActivityLaunchAnimator.INTERPOLATORS.copy(
-                positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
+            ActivityTransitionAnimator.INTERPOLATORS.copy(
+                positionXInterpolator =
+                    ActivityTransitionAnimator.INTERPOLATORS.positionInterpolator
             )
     }
 
@@ -319,9 +320,9 @@
     }
 
     /**
-     * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from the
-     * dialog that contains [View]. Note that the dialog must have been shown using this animator,
-     * otherwise this method will return null.
+     * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from
+     * the dialog that contains [View]. Note that the dialog must have been shown using this
+     * animator, otherwise this method will return null.
      *
      * The returned controller will take care of dismissing the dialog at the right time after the
      * activity started, when the dialog to app animation is done (or when it is cancelled). If this
@@ -333,7 +334,7 @@
     fun createActivityLaunchController(
         view: View,
         cujType: Int? = null,
-    ): ActivityLaunchAnimator.Controller? {
+    ): ActivityTransitionAnimator.Controller? {
         val animatedDialog =
             openedDialogs.firstOrNull {
                 it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl
@@ -343,7 +344,7 @@
     }
 
     /**
-     * Create an [ActivityLaunchAnimator.Controller] that can be used to launch an activity from
+     * Create an [ActivityTransitionAnimator.Controller] that can be used to launch an activity from
      * [dialog]. Note that the dialog must have been shown using this animator, otherwise this
      * method will return null.
      *
@@ -357,7 +358,7 @@
     fun createActivityLaunchController(
         dialog: Dialog,
         cujType: Int? = null,
-    ): ActivityLaunchAnimator.Controller? {
+    ): ActivityTransitionAnimator.Controller? {
         val animatedDialog = openedDialogs.firstOrNull { it.dialog == dialog } ?: return null
         return createActivityLaunchController(animatedDialog, cujType)
     }
@@ -365,7 +366,7 @@
     private fun createActivityLaunchController(
         animatedDialog: AnimatedDialog,
         cujType: Int? = null
-    ): ActivityLaunchAnimator.Controller? {
+    ): ActivityTransitionAnimator.Controller? {
         // At this point, we know that the intent of the caller is to dismiss the dialog to show
         // an app, so we disable the exit animation into the source because we will never want to
         // run it anyways.
@@ -384,12 +385,12 @@
 
         val dialogContentWithBackground = animatedDialog.dialogContentWithBackground ?: return null
         val controller =
-            ActivityLaunchAnimator.Controller.fromView(dialogContentWithBackground, cujType)
+            ActivityTransitionAnimator.Controller.fromView(dialogContentWithBackground, cujType)
                 ?: return null
 
         // Wrap the controller into one that will instantly dismiss the dialog when the animation is
         // done or dismiss it normally (fading it out) if the animation is cancelled.
-        return object : ActivityLaunchAnimator.Controller by controller {
+        return object : ActivityTransitionAnimator.Controller by controller {
             override val isDialogLaunch = true
 
             override fun onIntentStarted(willAnimate: Boolean) {
@@ -400,8 +401,8 @@
                 }
             }
 
-            override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
-                controller.onLaunchAnimationCancelled()
+            override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+                controller.onTransitionAnimationCancelled()
                 enableDialogDismiss()
                 dialog.dismiss()
             }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
index c49a487..2ba5948 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
@@ -21,14 +21,14 @@
 /** A piece of UI that can be expanded into a Dialog or an Activity. */
 interface Expandable {
     /**
-     * Create an [ActivityLaunchAnimator.Controller] that can be used to expand this [Expandable]
-     * into an Activity, or return `null` if this [Expandable] should not be animated (e.g. if it is
-     * currently not attached or visible).
+     * Create an [ActivityTransitionAnimator.Controller] that can be used to expand this
+     * [Expandable] into an Activity, or return `null` if this [Expandable] should not be animated
+     * (e.g. if it is currently not attached or visible).
      *
      * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
      *   associated to the launch that will use this controller.
      */
-    fun activityLaunchController(cujType: Int? = null): ActivityLaunchAnimator.Controller?
+    fun activityLaunchController(cujType: Int? = null): ActivityTransitionAnimator.Controller?
 
     /**
      * Create a [DialogLaunchAnimator.Controller] that can be used to expand this [Expandable] into
@@ -49,8 +49,8 @@
             return object : Expandable {
                 override fun activityLaunchController(
                     cujType: Int?,
-                ): ActivityLaunchAnimator.Controller? {
-                    return ActivityLaunchAnimator.Controller.fromView(view, cujType)
+                ): ActivityTransitionAnimator.Controller? {
+                    return ActivityTransitionAnimator.Controller.fromView(view, cujType)
                 }
 
                 override fun dialogLaunchController(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 03f10f9..f7148d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -42,15 +42,15 @@
 private const val TAG = "GhostedViewLaunchAnimatorController"
 
 /**
- * A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView]
- * of [ghostedView] as well as an expandable background view, which are drawn and animated instead
- * of the ghosted view.
+ * A base implementation of [ActivityTransitionAnimator.Controller] which creates a
+ * [ghost][GhostView] of [ghostedView] as well as an expandable background view, which are drawn and
+ * animated instead of the ghosted view.
  *
  * Important: [ghostedView] must be attached to a [ViewGroup] when calling this function and during
  * the animation. It must also implement [LaunchableView], otherwise an exception will be thrown
  * during this controller instantiation.
  *
- * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
+ * Note: Avoid instantiating this directly and call [ActivityTransitionAnimator.Controller.fromView]
  * whenever possible instead.
  */
 open class GhostedViewLaunchAnimatorController
@@ -63,7 +63,7 @@
     private val cujType: Int? = null,
     private var interactionJankMonitor: InteractionJankMonitor =
         InteractionJankMonitor.getInstance(),
-) : ActivityLaunchAnimator.Controller {
+) : ActivityTransitionAnimator.Controller {
 
     /** The container to which we will add the ghost view and expanding background. */
     override var transitionContainer = ghostedView.rootView as ViewGroup
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 1020263..84e5725 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -40,7 +40,7 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
@@ -135,7 +135,7 @@
         object : Expandable {
             override fun activityLaunchController(
                 cujType: Int?,
-            ): ActivityLaunchAnimator.Controller? {
+            ): ActivityTransitionAnimator.Controller? {
                 if (!isComposed.value) {
                     return null
                 }
@@ -176,7 +176,7 @@
                 linearProgress: Float
             ) {
                 // We copy state given that it's always the same object that is mutated by
-                // ActivityLaunchAnimator.
+                // ActivityTransitionAnimator.
                 animatorState.value =
                     TransitionAnimator.State(
                             state.top,
@@ -256,11 +256,11 @@
         }
     }
 
-    /** Create an [ActivityLaunchAnimator.Controller] that can be used to animate activities. */
-    private fun activityController(cujType: Int?): ActivityLaunchAnimator.Controller {
+    /** Create an [ActivityTransitionAnimator.Controller] that can be used to animate activities. */
+    private fun activityController(cujType: Int?): ActivityTransitionAnimator.Controller {
         val delegate = transitionController()
         return object :
-            ActivityLaunchAnimator.Controller, TransitionAnimator.Controller by delegate {
+            ActivityTransitionAnimator.Controller, TransitionAnimator.Controller by delegate {
             override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
                 delegate.onTransitionAnimationStart(isExpandingFullyAbove)
                 overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index ff53ff2..378a1e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -34,6 +34,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -43,23 +44,21 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val viewModel: LockscreenSceneViewModel,
+    viewModel: LockscreenSceneViewModel,
     private val lockscreenContent: Lazy<LockscreenContent>,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
     override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
-        viewModel.upDestinationSceneKey
-            .map { pageKey ->
-                destinationScenes(up = pageKey, left = viewModel.leftDestinationSceneKey)
-            }
+        combine(viewModel.upDestinationSceneKey, viewModel.leftDestinationSceneKey, ::Pair)
+            .map { (upKey, leftKey) -> destinationScenes(up = upKey, left = leftKey) }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
                 initialValue =
                     destinationScenes(
                         up = viewModel.upDestinationSceneKey.value,
-                        left = viewModel.leftDestinationSceneKey,
+                        left = viewModel.leftDestinationSceneKey.value,
                     )
             )
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index d70f82f..ef6ae2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -19,6 +19,7 @@
 
 import android.util.Log
 import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.WindowInsets
@@ -140,6 +141,8 @@
 ) {
     val density = LocalDensity.current
     val screenCornerRadius = LocalScreenCornerRadius.current
+    val scrollState = rememberScrollState()
+    val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
     val expansionFraction by viewModel.expandFraction.collectAsState(0f)
 
     val navBarHeight =
@@ -180,11 +183,28 @@
 
     // if contentHeight drops below minimum visible scrim height while scrim is
     // expanded, reset scrim offset.
-    LaunchedEffect(contentHeight, screenHeight, maxScrimTop, scrimOffset) {
+    LaunchedEffect(contentHeight, scrimOffset) {
         snapshotFlow { contentHeight.value < minVisibleScrimHeight() && scrimOffset.value < 0f }
             .collect { shouldCollapse -> if (shouldCollapse) scrimOffset.value = 0f }
     }
 
+    // if we receive scroll delta from NSSL, offset the scrim and placeholder accordingly.
+    LaunchedEffect(syntheticScroll, scrimOffset, scrollState) {
+        snapshotFlow { syntheticScroll.value }
+            .collect { delta ->
+                val minOffset = minScrimOffset()
+                if (scrimOffset.value > minOffset) {
+                    val remainingDelta = (minOffset - (scrimOffset.value - delta)).coerceAtLeast(0f)
+                    scrimOffset.value = (scrimOffset.value - delta).coerceAtLeast(minOffset)
+                    if (remainingDelta > 0f) {
+                        scrollState.scrollBy(remainingDelta)
+                    }
+                } else {
+                    scrollState.scrollTo(delta.roundToInt())
+                }
+            }
+    }
+
     Box(
         modifier =
             modifier
@@ -260,7 +280,7 @@
                                 )
                             }
                         )
-                        .verticalScroll(rememberScrollState())
+                        .verticalScroll(scrollState)
                         .fillMaxWidth()
                         .height { (contentHeight.value + navBarHeight).roundToInt() },
             )
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index b3d2bc9..c8fbad4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -46,7 +46,7 @@
     val draggable: DraggableHandler = SceneDraggableHandler(this)
 
     private var _swipeTransition: SwipeTransition? = null
-    internal var swipeTransition: SwipeTransition
+    private var swipeTransition: SwipeTransition
         get() = _swipeTransition ?: error("SwipeTransition needs to be initialized")
         set(value) {
             _swipeTransition = value
@@ -92,10 +92,6 @@
     /** The [Swipes] associated to the current gesture. */
     private var swipes: Swipes? = null
 
-    /** The [UserActionResult] associated to up and down swipes. */
-    private var upOrLeftResult: UserActionResult? = null
-    private var downOrRightResult: UserActionResult? = null
-
     /**
      * Whether we should immediately intercept a gesture.
      *
@@ -128,7 +124,7 @@
             // This [transition] was already driving the animation: simply take over it.
             // Stop animating and start from where the current offset.
             swipeTransition.cancelOffsetAnimation()
-            updateSwipesResults(swipeTransition._fromScene)
+            swipes!!.updateSwipesResults(swipeTransition._fromScene)
             return
         }
 
@@ -144,16 +140,24 @@
         }
 
         val fromScene = layoutImpl.scene(transitionState.currentScene)
-        updateSwipes(fromScene, startedPosition, pointersDown)
+        val newSwipes = computeSwipes(fromScene, startedPosition, pointersDown)
+        swipes = newSwipes
+        val result = newSwipes.findUserActionResult(fromScene, overSlop, true)
 
-        val result =
-            findUserActionResult(fromScene, directionOffset = overSlop, updateSwipesResults = true)
-                ?: return
-        updateTransition(SwipeTransition(fromScene, result), force = true)
-    }
+        // As we were unable to locate a valid target scene, the initial SwipeTransition cannot be
+        // defined.
+        if (result == null) return
 
-    private fun updateSwipes(fromScene: Scene, startedPosition: Offset?, pointersDown: Int) {
-        this.swipes = computeSwipes(fromScene, startedPosition, pointersDown)
+        val newSwipeTransition =
+            SwipeTransition(
+                fromScene = fromScene,
+                result = result,
+                swipes = newSwipes,
+                layoutImpl = layoutImpl,
+                orientation = orientation
+            )
+
+        updateTransition(newSwipeTransition, force = true)
     }
 
     private fun computeSwipes(
@@ -210,13 +214,6 @@
         }
     }
 
-    private fun Scene.getAbsoluteDistance(distance: UserActionDistance?): Float {
-        val targetSize = this.targetSize
-        return with(distance ?: DefaultSwipeDistance) {
-            layoutImpl.density.absoluteDistance(targetSize, orientation)
-        }
-    }
-
     internal fun onDrag(delta: Float) {
         if (delta == 0f || !isDrivingTransition) return
         swipeTransition.dragOffset += delta
@@ -226,15 +223,17 @@
 
         val isNewFromScene = fromScene.key != swipeTransition.fromScene
         val result =
-            findUserActionResult(
-                fromScene,
-                swipeTransition.dragOffset,
-                updateSwipesResults = isNewFromScene,
+            swipes!!.findUserActionResult(
+                fromScene = fromScene,
+                directionOffset = swipeTransition.dragOffset,
+                updateSwipesResults = isNewFromScene
             )
-                ?: run {
-                    onDragStopped(delta, true)
-                    return
-                }
+
+        if (result == null) {
+            onDragStopped(velocity = delta, canChangeScene = true)
+            return
+        }
+
         swipeTransition.dragOffset += acceleratedOffset
 
         if (
@@ -242,25 +241,20 @@
                 result.toScene != swipeTransition.toScene ||
                 result.transitionKey != swipeTransition.key
         ) {
-            updateTransition(
-                SwipeTransition(fromScene, result).apply {
-                    this.dragOffset = swipeTransition.dragOffset
-                }
-            )
+            val newSwipeTransition =
+                SwipeTransition(
+                        fromScene = fromScene,
+                        result = result,
+                        swipes = swipes!!,
+                        layoutImpl = layoutImpl,
+                        orientation = orientation
+                    )
+                    .apply { dragOffset = swipeTransition.dragOffset }
+
+            updateTransition(newSwipeTransition)
         }
     }
 
-    private fun updateSwipesResults(fromScene: Scene) {
-        val (upOrLeftResult, downOrRightResult) =
-            computeSwipesResults(
-                fromScene,
-                this.swipes ?: error("updateSwipes() should be called before updateSwipesResults()")
-            )
-
-        this.upOrLeftResult = upOrLeftResult
-        this.downOrRightResult = downOrRightResult
-    }
-
     private fun computeSwipesResults(
         fromScene: Scene,
         swipes: Swipes
@@ -295,74 +289,20 @@
 
         // If the swipe was not committed, don't do anything.
         if (swipeTransition._currentScene != toScene) {
-            return Pair(fromScene, 0f)
+            return fromScene to 0f
         }
 
         // If the offset is past the distance then let's change fromScene so that the user can swipe
         // to the next screen or go back to the previous one.
         val offset = swipeTransition.dragOffset
-        return if (offset <= -absoluteDistance && upOrLeftResult?.toScene == toScene.key) {
-            Pair(toScene, absoluteDistance)
-        } else if (offset >= absoluteDistance && downOrRightResult?.toScene == toScene.key) {
-            Pair(toScene, -absoluteDistance)
+        return if (offset <= -absoluteDistance && swipes!!.upOrLeftResult?.toScene == toScene.key) {
+            toScene to absoluteDistance
+        } else if (
+            offset >= absoluteDistance && swipes!!.downOrRightResult?.toScene == toScene.key
+        ) {
+            toScene to -absoluteDistance
         } else {
-            Pair(fromScene, 0f)
-        }
-    }
-
-    /**
-     * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset].
-     *
-     * @param fromScene the scene from which we look for the target
-     * @param directionOffset signed float that indicates the direction. Positive is down or right
-     *   negative is up or left.
-     * @param updateSwipesResults whether the target scenes should be updated to the current values
-     *   held in the Scenes map. Usually we don't want to update them while doing a drag, because
-     *   this could change the target scene (jump cutting) to a different scene, when some system
-     *   state changed the targets the background. However, an update is needed any time we
-     *   calculate the targets for a new fromScene.
-     * @return null when there are no targets in either direction. If one direction is null and you
-     *   drag into the null direction this function will return the opposite direction, assuming
-     *   that the users intention is to start the drag into the other direction eventually. If
-     *   [directionOffset] is 0f and both direction are available, it will default to
-     *   [upOrLeftResult].
-     */
-    private fun findUserActionResult(
-        fromScene: Scene,
-        directionOffset: Float,
-        updateSwipesResults: Boolean,
-    ): UserActionResult? {
-        if (updateSwipesResults) updateSwipesResults(fromScene)
-
-        return when {
-            upOrLeftResult == null && downOrRightResult == null -> null
-            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
-                upOrLeftResult
-            else -> downOrRightResult
-        }
-    }
-
-    /**
-     * A strict version of [findUserActionResult] that will return null when there is no Scene in
-     * [directionOffset] direction
-     */
-    private fun findUserActionResultStrict(directionOffset: Float): UserActionResult? {
-        return when {
-            directionOffset > 0f -> upOrLeftResult
-            directionOffset < 0f -> downOrRightResult
-            else -> null
-        }
-    }
-
-    private fun computeAbsoluteDistance(
-        fromScene: Scene,
-        result: UserActionResult,
-    ): Float {
-        return if (result == upOrLeftResult) {
-            -fromScene.getAbsoluteDistance(result.distance)
-        } else {
-            check(result == downOrRightResult)
-            fromScene.getAbsoluteDistance(result.distance)
+            fromScene to 0f
         }
     }
 
@@ -430,19 +370,24 @@
 
             if (startFromIdlePosition) {
                 // If there is a target scene, we start the overscroll animation.
-                val result =
-                    findUserActionResultStrict(velocity)
-                        ?: run {
-                            // We will not animate
-                            layoutState.finishTransition(swipeTransition, idleScene = fromScene.key)
-                            return
-                        }
+                val result = swipes!!.findUserActionResultStrict(velocity)
+                if (result == null) {
+                    // We will not animate
+                    layoutState.finishTransition(swipeTransition, idleScene = fromScene.key)
+                    return
+                }
 
-                updateTransition(
-                    SwipeTransition(fromScene, result).apply {
-                        _currentScene = swipeTransition._currentScene
-                    }
-                )
+                val newSwipeTransition =
+                    SwipeTransition(
+                            fromScene = fromScene,
+                            result = result,
+                            swipes = swipes!!,
+                            layoutImpl = layoutImpl,
+                            orientation = orientation
+                        )
+                        .apply { _currentScene = swipeTransition._currentScene }
+
+                updateTransition(newSwipeTransition)
                 animateTo(targetScene = fromScene, targetOffset = 0f)
             } else {
                 // We were between two scenes: animate to the initial scene.
@@ -486,134 +431,220 @@
         }
     }
 
-    private fun SwipeTransition(fromScene: Scene, result: UserActionResult): SwipeTransition {
-        return SwipeTransition(
-            result.transitionKey,
-            fromScene,
-            layoutImpl.scene(result.toScene),
-            computeAbsoluteDistance(fromScene, result),
-        )
-    }
-
-    internal class SwipeTransition(
-        val key: TransitionKey?,
-        val _fromScene: Scene,
-        val _toScene: Scene,
-        /**
-         * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is
-         * above or to the left of [toScene].
-         */
-        val distance: Float,
-    ) : TransitionState.Transition(_fromScene.key, _toScene.key) {
-        var _currentScene by mutableStateOf(_fromScene)
-        override val currentScene: SceneKey
-            get() = _currentScene.key
-
-        override val progress: Float
-            get() {
-                val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
-                return offset / distance
-            }
-
-        override val isInitiatedByUserInput = true
-
-        /** The current offset caused by the drag gesture. */
-        var dragOffset by mutableFloatStateOf(0f)
-
-        /**
-         * Whether the offset is animated (the user lifted their finger) or if it is driven by
-         * gesture.
-         */
-        var isAnimatingOffset by mutableStateOf(false)
-
-        // If we are not animating offset, it means the offset is being driven by the user's finger.
-        override val isUserInputOngoing: Boolean
-            get() = !isAnimatingOffset
-
-        /** The animatable used to animate the offset once the user lifted its finger. */
-        val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold)
-
-        /** Job to check that there is at most one offset animation in progress. */
-        private var offsetAnimationJob: Job? = null
-
-        /** The spec to use when animating this transition to either [fromScene] or [toScene]. */
-        lateinit var swipeSpec: SpringSpec<Float>
-
-        /** Ends any previous [offsetAnimationJob] and runs the new [job]. */
-        private fun startOffsetAnimation(job: () -> Job) {
-            cancelOffsetAnimation()
-            offsetAnimationJob = job()
-        }
-
-        /** Cancel any ongoing offset animation. */
-        // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
-        // the same time.
-        fun cancelOffsetAnimation() {
-            offsetAnimationJob?.cancel()
-            finishOffsetAnimation()
-        }
-
-        fun finishOffsetAnimation() {
-            if (isAnimatingOffset) {
-                isAnimatingOffset = false
-                dragOffset = offsetAnimatable.value
-            }
-        }
-
-        fun animateOffset(
-            // TODO(b/317063114) The CoroutineScope should be removed.
-            coroutineScope: CoroutineScope,
-            initialVelocity: Float,
-            targetOffset: Float,
-            onAnimationCompleted: () -> Unit,
-        ) {
-            startOffsetAnimation {
-                coroutineScope.launch {
-                    animateOffset(targetOffset, initialVelocity)
-                    onAnimationCompleted()
-                }
-            }
-        }
-
-        private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) {
-            if (!isAnimatingOffset) {
-                offsetAnimatable.snapTo(dragOffset)
-            }
-            isAnimatingOffset = true
-
-            offsetAnimatable.animateTo(
-                targetValue = targetOffset,
-                animationSpec = swipeSpec,
-                initialVelocity = initialVelocity,
-            )
-
-            finishOffsetAnimation()
-        }
-    }
-
     companion object {
         private const val TAG = "SceneGestureHandler"
     }
+}
 
-    private object DefaultSwipeDistance : UserActionDistance {
-        override fun Density.absoluteDistance(
-            fromSceneSize: IntSize,
-            orientation: Orientation,
-        ): Float {
-            return when (orientation) {
-                Orientation.Horizontal -> fromSceneSize.width
-                Orientation.Vertical -> fromSceneSize.height
-            }.toFloat()
+private fun SwipeTransition(
+    fromScene: Scene,
+    result: UserActionResult,
+    swipes: Swipes,
+    layoutImpl: SceneTransitionLayoutImpl,
+    orientation: Orientation,
+): SwipeTransition {
+    val upOrLeftResult = swipes.upOrLeftResult
+    val downOrRightResult = swipes.downOrRightResult
+    val userActionDistance = result.distance ?: DefaultSwipeDistance
+    val absoluteDistance =
+        with(userActionDistance) {
+            layoutImpl.density.absoluteDistance(fromScene.targetSize, orientation)
+        }
+
+    return SwipeTransition(
+        key = result.transitionKey,
+        _fromScene = fromScene,
+        _toScene = layoutImpl.scene(result.toScene),
+        distance =
+            when (result) {
+                upOrLeftResult -> -absoluteDistance
+                downOrRightResult -> absoluteDistance
+                else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
+            },
+    )
+}
+
+private class SwipeTransition(
+    val key: TransitionKey?,
+    val _fromScene: Scene,
+    val _toScene: Scene,
+    /**
+     * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above
+     * or to the left of [toScene]
+     */
+    val distance: Float,
+) : TransitionState.Transition(_fromScene.key, _toScene.key) {
+    var _currentScene by mutableStateOf(_fromScene)
+    override val currentScene: SceneKey
+        get() = _currentScene.key
+
+    override val progress: Float
+        get() {
+            val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
+            return offset / distance
+        }
+
+    override val isInitiatedByUserInput = true
+
+    /** The current offset caused by the drag gesture. */
+    var dragOffset by mutableFloatStateOf(0f)
+
+    /**
+     * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture.
+     */
+    var isAnimatingOffset by mutableStateOf(false)
+
+    // If we are not animating offset, it means the offset is being driven by the user's finger.
+    override val isUserInputOngoing: Boolean
+        get() = !isAnimatingOffset
+
+    /** The animatable used to animate the offset once the user lifted its finger. */
+    val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold)
+
+    /** Job to check that there is at most one offset animation in progress. */
+    private var offsetAnimationJob: Job? = null
+
+    /** The spec to use when animating this transition to either [fromScene] or [toScene]. */
+    lateinit var swipeSpec: SpringSpec<Float>
+
+    /** Ends any previous [offsetAnimationJob] and runs the new [job]. */
+    private fun startOffsetAnimation(job: () -> Job) {
+        cancelOffsetAnimation()
+        offsetAnimationJob = job()
+    }
+
+    /** Cancel any ongoing offset animation. */
+    // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
+    // the same time.
+    fun cancelOffsetAnimation() {
+        offsetAnimationJob?.cancel()
+        finishOffsetAnimation()
+    }
+
+    fun finishOffsetAnimation() {
+        if (isAnimatingOffset) {
+            isAnimatingOffset = false
+            dragOffset = offsetAnimatable.value
         }
     }
 
-    /** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
-    private class Swipes(
-        val upOrLeft: Swipe?,
-        val downOrRight: Swipe?,
-        val upOrLeftNoSource: Swipe?,
-        val downOrRightNoSource: Swipe?,
-    )
+    fun animateOffset(
+        // TODO(b/317063114) The CoroutineScope should be removed.
+        coroutineScope: CoroutineScope,
+        initialVelocity: Float,
+        targetOffset: Float,
+        onAnimationCompleted: () -> Unit,
+    ) {
+        startOffsetAnimation {
+            coroutineScope.launch {
+                animateOffset(targetOffset, initialVelocity)
+                onAnimationCompleted()
+            }
+        }
+    }
+
+    private suspend fun animateOffset(targetOffset: Float, initialVelocity: Float) {
+        if (!isAnimatingOffset) {
+            offsetAnimatable.snapTo(dragOffset)
+        }
+        isAnimatingOffset = true
+
+        offsetAnimatable.animateTo(
+            targetValue = targetOffset,
+            animationSpec = swipeSpec,
+            initialVelocity = initialVelocity,
+        )
+
+        finishOffsetAnimation()
+    }
+}
+
+private object DefaultSwipeDistance : UserActionDistance {
+    override fun Density.absoluteDistance(
+        fromSceneSize: IntSize,
+        orientation: Orientation,
+    ): Float {
+        return when (orientation) {
+            Orientation.Horizontal -> fromSceneSize.width
+            Orientation.Vertical -> fromSceneSize.height
+        }.toFloat()
+    }
+}
+
+/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
+private class Swipes(
+    val upOrLeft: Swipe?,
+    val downOrRight: Swipe?,
+    val upOrLeftNoSource: Swipe?,
+    val downOrRightNoSource: Swipe?,
+) {
+    /** The [UserActionResult] associated to up and down swipes. */
+    var upOrLeftResult: UserActionResult? = null
+    var downOrRightResult: UserActionResult? = null
+
+    fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> {
+        val userActions = fromScene.userActions
+        fun result(swipe: Swipe?): UserActionResult? {
+            return userActions[swipe ?: return null]
+        }
+
+        val upOrLeftResult = result(upOrLeft) ?: result(upOrLeftNoSource)
+        val downOrRightResult = result(downOrRight) ?: result(downOrRightNoSource)
+        return upOrLeftResult to downOrRightResult
+    }
+
+    fun updateSwipesResults(fromScene: Scene) {
+        val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromScene)
+
+        this.upOrLeftResult = upOrLeftResult
+        this.downOrRightResult = downOrRightResult
+    }
+
+    /**
+     * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset].
+     *
+     * @param fromScene the scene from which we look for the target
+     * @param directionOffset signed float that indicates the direction. Positive is down or right
+     *   negative is up or left.
+     * @param updateSwipesResults whether the target scenes should be updated to the current values
+     *   held in the Scenes map. Usually we don't want to update them while doing a drag, because
+     *   this could change the target scene (jump cutting) to a different scene, when some system
+     *   state changed the targets the background. However, an update is needed any time we
+     *   calculate the targets for a new fromScene.
+     * @return null when there are no targets in either direction. If one direction is null and you
+     *   drag into the null direction this function will return the opposite direction, assuming
+     *   that the users intention is to start the drag into the other direction eventually. If
+     *   [directionOffset] is 0f and both direction are available, it will default to
+     *   [upOrLeftResult].
+     */
+    fun findUserActionResult(
+        fromScene: Scene,
+        directionOffset: Float,
+        updateSwipesResults: Boolean,
+    ): UserActionResult? {
+        if (updateSwipesResults) {
+            updateSwipesResults(fromScene)
+        }
+
+        return when {
+            upOrLeftResult == null && downOrRightResult == null -> null
+            (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
+                upOrLeftResult
+            else -> downOrRightResult
+        }
+    }
+
+    /**
+     * A strict version of [findUserActionResult] that will return null when there is no Scene in
+     * [directionOffset] direction
+     */
+    fun findUserActionResultStrict(directionOffset: Float): UserActionResult? {
+        return when {
+            directionOffset > 0f -> upOrLeftResult
+            directionOffset < 0f -> downOrRightResult
+            else -> null
+        }
+    }
 }
 
 private class SceneDraggableHandler(
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index dacbdb4..c91d298 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -127,6 +127,9 @@
         val progress: Float
             get() = (transitionState as Transition).progress
 
+        val isUserInputOngoing: Boolean
+            get() = (transitionState as Transition).isUserInputOngoing
+
         fun advanceUntilIdle() {
             testScope.testScheduler.advanceUntilIdle()
         }
@@ -538,12 +541,11 @@
         onDragStopped(velocity = velocityThreshold)
 
         assertTransition(currentScene = SceneC)
-        assertThat(sceneGestureHandler.isDrivingTransition).isTrue()
-        assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isTrue()
+        assertThat(isUserInputOngoing).isFalse()
 
         // Start a new gesture while the offset is animating
         onDragStartedImmediately()
-        assertThat(sceneGestureHandler.swipeTransition.isAnimatingOffset).isFalse()
+        assertThat(isUserInputOngoing).isTrue()
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index f7743e2..259f349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -37,7 +37,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel
@@ -106,7 +106,7 @@
     @Mock private lateinit var udfpsController: UdfpsController
     @Mock private lateinit var udfpsView: UdfpsView
     @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
-    @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+    @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
     @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -167,7 +167,7 @@
                 reason,
                 controllerCallback,
                 onTouch,
-                activityLaunchAnimator,
+                mActivityTransitionAnimator,
                 primaryBouncerInteractor,
                 alternateBouncerInteractor,
                 isDebuggable,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 90c3c14..529403a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -75,7 +75,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
 import com.android.systemui.biometrics.udfps.InteractionEvent;
@@ -203,7 +203,7 @@
     @Mock
     private SystemUIDialogManager mSystemUIDialogManager;
     @Mock
-    private ActivityLaunchAnimator mActivityLaunchAnimator;
+    private ActivityTransitionAnimator mActivityTransitionAnimator;
     @Mock
     private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     @Mock
@@ -331,7 +331,7 @@
                 mUnlockedScreenOffAnimationController,
                 mSystemUIDialogManager,
                 mLatencyTracker,
-                mActivityLaunchAnimator,
+                mActivityTransitionAnimator,
                 mBiometricExecutor,
                 mPrimaryBouncerInteractor,
                 mShadeInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 7d9c2f9..324534f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -26,7 +26,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -70,7 +70,7 @@
     protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     protected @Mock SystemUIDialogManager mDialogManager;
     protected @Mock UdfpsController mUdfpsController;
-    protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+    protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
     protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     protected @Mock ShadeInteractor mShadeInteractor;
     protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -148,7 +148,7 @@
                 mUnlockedScreenOffAnimationController,
                 mDialogManager,
                 mUdfpsController,
-                mActivityLaunchAnimator,
+                mActivityTransitionAnimator,
                 mPrimaryBouncerInteractor,
                 mAlternateBouncerInteractor,
                 mUdfpsKeyguardAccessibilityDelegate,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index bd9ca30..b4e2eab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -16,26 +16,18 @@
 
 package com.android.systemui.communal.data.repository
 
-import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
@@ -48,37 +40,20 @@
 class CommunalRepositoryImplTest : SysuiTestCase() {
     private lateinit var underTest: CommunalRepositoryImpl
 
-    private lateinit var secureSettings: FakeSettings
-    private lateinit var userRepository: FakeUserRepository
-
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneContainerRepository = kosmos.sceneContainerRepository
 
     @Before
     fun setUp() {
-        secureSettings = FakeSettings()
-        userRepository = kosmos.fakeUserRepository
-
-        val listOfUserInfo = listOf(MAIN_USER_INFO)
-        userRepository.setUserInfos(listOfUserInfo)
-
-        kosmos.fakeFeatureFlagsClassic.apply { set(Flags.COMMUNAL_SERVICE_ENABLED, true) }
-        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
         underTest = createRepositoryImpl(false)
     }
 
     private fun createRepositoryImpl(sceneContainerEnabled: Boolean): CommunalRepositoryImpl {
         return CommunalRepositoryImpl(
             testScope.backgroundScope,
-            testScope.backgroundScope,
-            kosmos.testDispatcher,
-            kosmos.fakeFeatureFlagsClassic,
             kosmos.fakeSceneContainerFlags.apply { enabled = sceneContainerEnabled },
             sceneContainerRepository,
-            kosmos.fakeUserRepository,
-            secureSettings,
         )
     }
 
@@ -159,29 +134,4 @@
             assertThat(transitionState)
                 .isEqualTo(ObservableCommunalTransitionState.Idle(CommunalSceneKey.DEFAULT))
         }
-
-    @Test
-    fun communalEnabledState_false_whenGlanceableHubSettingFalse() =
-        testScope.runTest {
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, MAIN_USER_INFO.id)
-
-            val communalEnabled by collectLastValue(underTest.communalEnabledState)
-            assertThat(communalEnabled).isFalse()
-        }
-
-    @Test
-    fun communalEnabledState_true_whenGlanceableHubSettingTrue() =
-        testScope.runTest {
-            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            secureSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, MAIN_USER_INFO.id)
-
-            val communalEnabled by collectLastValue(underTest.communalEnabledState)
-            assertThat(communalEnabled).isTrue()
-        }
-
-    companion object {
-        private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
-        private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
new file mode 100644
index 0000000..0aca16d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.app.admin.devicePolicyManager
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var underTest: CommunalSettingsRepository
+
+    @Before
+    fun setUp() {
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+        setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+        setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
+        underTest = kosmos.communalSettingsRepository
+    }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun secondaryUserIsInvalid() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
+
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_INVALID_USER)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun classicFlagIsDisabled() =
+        testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+        }
+
+    @DisableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun communalHubFlagIsDisabled() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByUser() =
+        testScope.runTest {
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
+            assertThat(enabledState?.enabled).isFalse()
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
+            assertThat(enabledState?.enabled).isTrue()
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByDevicePolicy() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isTrue()
+
+            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_DEVICE_POLICY)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubIsDisabledByUserAndDevicePolicy() =
+        testScope.runTest {
+            val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
+            assertThat(enabledState?.enabled).isTrue()
+
+            kosmos.fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
+            setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
+
+            assertThat(enabledState?.enabled).isFalse()
+            assertThat(enabledState)
+                .containsExactly(
+                    DisabledReason.DISABLED_REASON_DEVICE_POLICY,
+                    DisabledReason.DISABLED_REASON_USER_SETTING,
+                )
+        }
+
+    private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
+        whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
+            .thenReturn(disabledFlags)
+        kosmos.broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+        )
+    }
+
+    private companion object {
+        val PRIMARY_USER =
+            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+        val SECONDARY_USER = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 6a3fc2a..824733b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -19,6 +19,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -59,7 +60,7 @@
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         keyguardRepository = kosmos.fakeKeyguardRepository
 
-        communalRepository.setIsCommunalEnabled(false)
+        mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
 
         underTest = kosmos.communalInteractor
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index c5485c5..3ac19e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -23,6 +23,7 @@
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
@@ -41,6 +42,8 @@
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -109,12 +112,19 @@
         whenever(secondaryUser.isMain).thenReturn(false)
         userRepository.setUserInfos(listOf(mainUser, secondaryUser))
 
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+
         underTest = kosmos.communalInteractor
     }
 
     @Test
     fun communalEnabled_true() =
-        testScope.runTest { assertThat(underTest.isCommunalEnabled).isTrue() }
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(mainUser)
+            runCurrent()
+            assertThat(underTest.isCommunalEnabled).isTrue()
+        }
 
     @Test
     fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
@@ -125,7 +135,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isTrue()
         }
@@ -139,7 +148,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(true)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isFalse()
         }
@@ -153,7 +161,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(secondaryUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isFalse()
         }
@@ -167,7 +174,6 @@
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setDreaming(true)
-            communalRepository.setCommunalEnabledState(true)
 
             assertThat(isAvailable).isTrue()
         }
@@ -175,13 +181,14 @@
     @Test
     fun isCommunalAvailable_communalDisabled_false() =
         testScope.runTest {
+            mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB)
+
             val isAvailable by collectLastValue(underTest.isCommunalAvailable)
             assertThat(isAvailable).isFalse()
 
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(mainUser)
             keyguardRepository.setKeyguardShowing(true)
-            communalRepository.setCommunalEnabledState(false)
 
             assertThat(isAvailable).isFalse()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 6c87e0f..ceb7fac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -22,12 +22,15 @@
 import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -35,11 +38,13 @@
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -62,6 +67,8 @@
         userRepository = kosmos.fakeUserRepository
 
         userRepository.setUserInfos(listOf(MAIN_USER_INFO))
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         underTest = kosmos.communalTutorialInteractor
     }
@@ -127,6 +134,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -139,6 +147,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -151,6 +160,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
             communalRepository.setIsCommunalHubShowing(true)
@@ -163,6 +173,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
 
             communalRepository.setIsCommunalHubShowing(false)
@@ -175,6 +186,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalRepository.setIsCommunalHubShowing(true)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
 
@@ -188,6 +200,7 @@
         testScope.runTest {
             val tutorialSettingState by
                 collectLastValue(communalTutorialRepository.tutorialSettingState)
+            userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             communalRepository.setIsCommunalHubShowing(true)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
@@ -198,14 +211,11 @@
 
     private suspend fun setCommunalAvailable(available: Boolean) {
         if (available) {
-            communalRepository.setIsCommunalEnabled(true)
-            communalRepository.setCommunalEnabledState(true)
             keyguardRepository.setIsEncryptedOrLockdown(false)
             userRepository.setSelectedUserInfo(MAIN_USER_INFO)
             keyguardRepository.setKeyguardShowing(true)
         } else {
-            communalRepository.setIsCommunalEnabled(false)
-            communalRepository.setCommunalEnabledState(false)
+            keyguardRepository.setIsEncryptedOrLockdown(true)
         }
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 73d3091..f70b6a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -22,12 +22,12 @@
 import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
@@ -37,6 +37,8 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -92,7 +94,8 @@
         mediaRepository = kosmos.fakeCommunalMediaRepository
         userRepository = kosmos.fakeUserRepository
 
-        kosmos.fakeCommunalRepository.setCommunalEnabledState(true)
+        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         underTest =
             CommunalViewModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 032d76f..8488843 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -19,12 +19,15 @@
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_ENABLED
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
@@ -33,6 +36,7 @@
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -62,6 +66,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+        kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+        mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
         appWidgetIdToRemove = MutableSharedFlow()
         whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
@@ -169,7 +175,8 @@
             fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
             fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
             fakeKeyguardRepository.setKeyguardShowing(true)
-            fakeCommunalRepository.setCommunalEnabledState(available)
+            val settingsValue = if (available) 1 else 0
+            fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
         }
 
     private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index a613ad8..0768340 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -23,14 +23,14 @@
 import android.service.quickaccesswallet.WalletCard
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -76,26 +76,28 @@
     }
 
     @Test
-    fun affordance_keyguardShowing_hasWalletCard_visibleModel() = testScope.runTest {
-        setUpState()
+    fun affordance_keyguardShowing_hasWalletCard_visibleModel() =
+        testScope.runTest {
+            setUpState()
 
-        val latest by collectLastValue(underTest.lockScreenState)
+            val latest by collectLastValue(underTest.lockScreenState)
 
-        val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
-        assertThat(visibleModel.icon)
-            .isEqualTo(
-                Icon.Loaded(
-                    drawable = ICON,
-                    contentDescription =
-                        ContentDescription.Resource(
-                            res = R.string.accessibility_wallet_button,
-                        ),
+            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+            assertThat(visibleModel.icon)
+                .isEqualTo(
+                    Icon.Loaded(
+                        drawable = ICON,
+                        contentDescription =
+                            ContentDescription.Resource(
+                                res = R.string.accessibility_wallet_button,
+                            ),
+                    )
                 )
-            )
-    }
+        }
 
     @Test
-    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = testScope.runTest {
+    fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() =
+        testScope.runTest {
             setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT)
 
             val latest by collectLastValue(underTest.lockScreenState)
@@ -104,54 +106,58 @@
         }
 
     @Test
-    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = testScope.runTest {
-        setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
+    fun affordance_keyguardShowing_hasPaymentCard_visibleModel() =
+        testScope.runTest {
+            setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT)
 
-        val latest by collectLastValue(underTest.lockScreenState)
+            val latest by collectLastValue(underTest.lockScreenState)
 
-        val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
-        assertThat(visibleModel.icon)
-            .isEqualTo(
-                Icon.Loaded(
-                    drawable = ICON,
-                    contentDescription =
-                        ContentDescription.Resource(
-                            res = R.string.accessibility_wallet_button,
-                        ),
+            val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible
+            assertThat(visibleModel.icon)
+                .isEqualTo(
+                    Icon.Loaded(
+                        drawable = ICON,
+                        contentDescription =
+                            ContentDescription.Resource(
+                                res = R.string.accessibility_wallet_button,
+                            ),
+                    )
                 )
-            )
-    }
+        }
 
     @Test
-    fun affordance_walletFeatureNotEnabled_modelIsNone() = testScope.runTest {
-        setUpState(isWalletFeatureAvailable = false)
+    fun affordance_walletFeatureNotEnabled_modelIsNone() =
+        testScope.runTest {
+            setUpState(isWalletFeatureAvailable = false)
 
-        val latest by collectLastValue(underTest.lockScreenState)
+            val latest by collectLastValue(underTest.lockScreenState)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-    }
+            assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun affordance_queryNotSuccessful_modelIsNone() = testScope.runTest {
-        setUpState(isWalletQuerySuccessful = false)
+    fun affordance_queryNotSuccessful_modelIsNone() =
+        testScope.runTest {
+            setUpState(isWalletQuerySuccessful = false)
 
-        val latest by collectLastValue(underTest.lockScreenState)
+            val latest by collectLastValue(underTest.lockScreenState)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-    }
+            assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
-    fun affordance_noSelectedCard_modelIsNone() = testScope.runTest {
-        setUpState(hasSelectedCard = false)
+    fun affordance_noSelectedCard_modelIsNone() =
+        testScope.runTest {
+            setUpState(hasSelectedCard = false)
 
-        val latest by collectLastValue(underTest.lockScreenState)
+            val latest by collectLastValue(underTest.lockScreenState)
 
-        assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
-    }
+            assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
 
     @Test
     fun onQuickAffordanceTriggered() {
-        val animationController: ActivityLaunchAnimator.Controller = mock()
+        val animationController: ActivityTransitionAnimator.Controller = mock()
         val expandable: Expandable = mock {
             whenever(this.activityLaunchController()).thenReturn(animationController)
         }
@@ -167,42 +173,46 @@
     }
 
     @Test
-    fun getPickerScreenState_default() = testScope.runTest {
-        setUpState()
+    fun getPickerScreenState_default() =
+        testScope.runTest {
+            setUpState()
 
-        assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
+        }
 
     @Test
-    fun getPickerScreenState_unavailable() = testScope.runTest {
-        setUpState(
-            isWalletServiceAvailable = false,
-        )
+    fun getPickerScreenState_unavailable() =
+        testScope.runTest {
+            setUpState(
+                isWalletServiceAvailable = false,
+            )
 
-        assertThat(underTest.getPickerScreenState())
-            .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+        }
 
     @Test
-    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = testScope.runTest {
-        setUpState(
-            isWalletFeatureAvailable = false,
-        )
+    fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() =
+        testScope.runTest {
+            setUpState(
+                isWalletFeatureAvailable = false,
+            )
 
-        assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
+        }
 
     @Test
-    fun getPickerScreenState_disabledWhenThereIsNoCard() = testScope.runTest {
-        setUpState(
-            hasSelectedCard = false,
-        )
+    fun getPickerScreenState_disabledWhenThereIsNoCard() =
+        testScope.runTest {
+            setUpState(
+                hasSelectedCard = false,
+            )
 
-        assertThat(underTest.getPickerScreenState())
-            .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
-    }
+            assertThat(underTest.getPickerScreenState())
+                .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java)
+        }
 
     private fun setUpState(
         isWalletFeatureAvailable: Boolean = true,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 4595fbf..7261723 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,22 +18,28 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.content.pm.UserInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,9 +55,7 @@
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
 
-    private val underTest by lazy {
-        createLockscreenSceneViewModel()
-    }
+    private val underTest by lazy { createLockscreenSceneViewModel() }
 
     @Test
     fun upTransitionSceneKey_canSwipeToUnlock_gone() =
@@ -80,29 +84,37 @@
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
         }
 
+    @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun leftTransitionSceneKey_communalIsEnabled_communal() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setIsCommunalEnabled(true)
-            val underTest = createLockscreenSceneViewModel()
-
-            assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
+            with(kosmos.fakeUserRepository) {
+                setUserInfos(listOf(PRIMARY_USER))
+                setSelectedUserInfo(PRIMARY_USER)
+            }
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+            val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+            assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
         }
 
+    @DisableFlags(FLAG_COMMUNAL_HUB)
     @Test
     fun leftTransitionSceneKey_communalIsDisabled_null() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setIsCommunalEnabled(false)
-            val underTest = createLockscreenSceneViewModel()
-
-            assertThat(underTest.leftDestinationSceneKey).isNull()
+            with(kosmos.fakeUserRepository) {
+                setUserInfos(listOf(PRIMARY_USER))
+                setSelectedUserInfo(PRIMARY_USER)
+            }
+            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+            val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
+            assertThat(leftDestinationSceneKey).isNull()
         }
 
     private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
         return LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = kosmos.deviceEntryInteractor,
-            communalInteractor = kosmos.communalInteractor,
+            communalSettingsInteractor = kosmos.communalSettingsInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
@@ -110,4 +122,9 @@
             notifications = kosmos.notificationsPlaceholderViewModel,
         )
     }
+
+    private companion object {
+        val PRIMARY_USER =
+            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9d3f0d6..006f429 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@
 import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
 import com.android.systemui.classifier.domain.interactor.falsingInteractor
 import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -130,7 +130,7 @@
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
     private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
-    private val communalInteractor by lazy { kosmos.communalInteractor }
+    private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
 
     private val transitionState by lazy {
         MutableStateFlow<ObservableTransitionState>(
@@ -155,7 +155,7 @@
         LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = deviceEntryInteractor,
-            communalInteractor = communalInteractor,
+            communalSettingsInteractor = communalSettingsInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
index cc4ebd4..c01f1c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt
@@ -27,7 +27,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.ActivityIntentHelper
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.keyguard.KeyguardViewMediator
@@ -78,7 +78,7 @@
     @Mock private lateinit var shadeController: ShadeController
     @Mock private lateinit var shadeViewController: ShadeViewController
     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
-    @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+    @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
     @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager
     @Mock private lateinit var statusBarWindowController: StatusBarWindowController
     @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController
@@ -109,7 +109,7 @@
                 shadeAnimationInteractor,
                 Lazy { statusBarKeyguardViewManager },
                 Lazy { notifShadeWindowController },
-                activityLaunchAnimator,
+                mActivityTransitionAnimator,
                 context,
                 DISPLAY_ID,
                 lockScreenUserManager,
@@ -149,7 +149,7 @@
                 override fun setShouldBlockVisibilityChanges(block: Boolean) {}
             }
         parent.addView(view)
-        val controller = ActivityLaunchAnimator.Controller.fromView(view)
+        val controller = ActivityTransitionAnimator.Controller.fromView(view)
         whenever(pendingIntent.isActivity).thenReturn(true)
         whenever(keyguardStateController.isShowing).thenReturn(true)
         whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
@@ -163,7 +163,7 @@
         )
         mainExecutor.runAllReady()
 
-        verify(activityLaunchAnimator)
+        verify(mActivityTransitionAnimator)
             .startPendingIntentWithAnimation(
                 nullable(),
                 eq(true),
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 6434209..1126ec3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -20,7 +20,7 @@
 import android.os.UserHandle;
 import android.view.View;
 
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 /**
@@ -54,7 +54,7 @@
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
             Runnable intentSentUiThreadCallback,
-            @Nullable ActivityLaunchAnimator.Controller animationController);
+            @Nullable ActivityTransitionAnimator.Controller animationController);
 
     /**
      * Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching
@@ -64,7 +64,7 @@
      */
     void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
             @Nullable Runnable intentSentUiThreadCallback,
-            @Nullable ActivityLaunchAnimator.Controller animationController);
+            @Nullable ActivityTransitionAnimator.Controller animationController);
 
     /**
      * The intent flag can be specified in startActivity().
@@ -72,26 +72,26 @@
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
     void startActivity(Intent intent, boolean dismissShade);
     default void startActivity(Intent intent, boolean dismissShade,
-            @Nullable ActivityLaunchAnimator.Controller animationController) {
+            @Nullable ActivityTransitionAnimator.Controller animationController) {
         startActivity(intent, dismissShade, animationController,
                 false /* showOverLockscreenWhenLocked */);
     }
 
     void startActivity(Intent intent, boolean dismissShade,
-            @Nullable ActivityLaunchAnimator.Controller animationController,
+            @Nullable ActivityTransitionAnimator.Controller animationController,
             boolean showOverLockscreenWhenLocked);
     void startActivity(Intent intent, boolean dismissShade,
-            @Nullable ActivityLaunchAnimator.Controller animationController,
+            @Nullable ActivityTransitionAnimator.Controller animationController,
             boolean showOverLockscreenWhenLocked, UserHandle userHandle);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
     void postStartActivityDismissingKeyguard(Intent intent, int delay);
     void postStartActivityDismissingKeyguard(Intent intent, int delay,
-            @Nullable ActivityLaunchAnimator.Controller animationController);
+            @Nullable ActivityTransitionAnimator.Controller animationController);
 
     /** Posts a start activity intent that dismisses keyguard. */
     void postStartActivityDismissingKeyguard(Intent intent, int delay,
-            @Nullable ActivityLaunchAnimator.Controller animationController,
+            @Nullable ActivityTransitionAnimator.Controller animationController,
             @Nullable String customMessage);
     void postStartActivityDismissingKeyguard(PendingIntent intent);
 
@@ -100,7 +100,7 @@
      * animation controller that should be used for the activity launch animation.
      */
     void postStartActivityDismissingKeyguard(PendingIntent intent,
-            @Nullable ActivityLaunchAnimator.Controller animationController);
+            @Nullable ActivityTransitionAnimator.Controller animationController);
 
     void postQSRunnableDismissingKeyguard(Runnable runnable);
 
@@ -123,7 +123,7 @@
             boolean disallowEnterPictureInPictureWhileLaunching,
             Callback callback,
             int flags,
-            @Nullable ActivityLaunchAnimator.Controller animationController,
+            @Nullable ActivityTransitionAnimator.Controller animationController,
             UserHandle userHandle);
 
     /** Execute a runnable after dismissing keyguard. */
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 15688c5..b30e4a2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1932,7 +1932,7 @@
     <!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] -->
     <string name="group_system_lock_screen">Lock screen</string>
     <!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
-    <string name="group_system_quick_memo">Open notes</string>
+    <string name="group_system_quick_memo">Take a note</string>
 
     <!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
     <string name="keyboard_shortcut_group_system_multitasking">System multitasking</string>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
index bf121fb..e57323b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuMessageView.java
@@ -26,6 +26,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.text.Layout;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -162,6 +163,7 @@
         mTextView.setPadding(/* left= */ 0, textPadding, /* right= */ 0, textPadding);
         mTextView.setTextSize(COMPLEX_UNIT_PX, textSize);
         mTextView.setTextColor(textColor);
+        mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
 
         final ColorStateList colorAccent = Utils.getColorAccent(getContext());
         mUndoButton.setText(res.getString(R.string.accessibility_floating_button_undo));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 66fe4b3..716209d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -66,7 +66,7 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.dagger.BiometricsBackground;
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -162,7 +162,7 @@
             mUnlockedScreenOffAnimationController;
     @NonNull private final LatencyTracker mLatencyTracker;
     @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
-    @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    @NonNull private final ActivityTransitionAnimator mActivityTransitionAnimator;
     @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     @NonNull private final ShadeInteractor mShadeInteractor;
     @Nullable private final TouchProcessor mTouchProcessor;
@@ -287,7 +287,7 @@
                             event,
                             fromUdfpsView
                         ),
-                        mActivityLaunchAnimator,
+                            mActivityTransitionAnimator,
                         mPrimaryBouncerInteractor,
                         mAlternateBouncerInteractor,
                         mUdfpsKeyguardAccessibilityDelegate,
@@ -663,7 +663,7 @@
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager dialogManager,
             @NonNull LatencyTracker latencyTracker,
-            @NonNull ActivityLaunchAnimator activityLaunchAnimator,
+            @NonNull ActivityTransitionAnimator activityTransitionAnimator,
             @NonNull @BiometricsBackground Executor biometricsExecutor,
             @NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
             @NonNull ShadeInteractor shadeInteractor,
@@ -706,7 +706,7 @@
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLatencyTracker = latencyTracker;
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mActivityTransitionAnimator = activityTransitionAnimator;
         mSensorProps = new FingerprintSensorPropertiesInternal(
                 -1 /* sensorId */,
                 SensorProperties.STRENGTH_CONVENIENCE,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 4ea5f4c..a209eae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,7 +44,7 @@
 import androidx.annotation.LayoutRes
 import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.biometrics.ui.binder.UdfpsTouchOverlayBinder
@@ -100,7 +100,7 @@
     @RequestReason val requestReason: Int,
     private val controllerCallback: IUdfpsOverlayControllerCallback,
     private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
-    private val activityLaunchAnimator: ActivityLaunchAnimator,
+    private val activityTransitionAnimator: ActivityTransitionAnimator,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
@@ -304,7 +304,7 @@
                     unlockedScreenOffAnimationController,
                     dialogManager,
                     controller,
-                    activityLaunchAnimator,
+                    activityTransitionAnimator,
                     primaryBouncerInteractor,
                     alternateBouncerInteractor,
                     udfpsKeyguardAccessibilityDelegate,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 7020d05..018d92e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -25,7 +25,7 @@
 import com.android.app.animation.Interpolators
 import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -69,7 +69,7 @@
     private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
     systemUIDialogManager: SystemUIDialogManager,
     private val udfpsController: UdfpsController,
-    private val activityLaunchAnimator: ActivityLaunchAnimator,
+    private val activityTransitionAnimator: ActivityTransitionAnimator,
     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
@@ -137,20 +137,20 @@
             }
         }
 
-    private val activityLaunchAnimatorListener: ActivityLaunchAnimator.Listener =
-        object : ActivityLaunchAnimator.Listener {
-            override fun onLaunchAnimationStart() {
+    private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener =
+        object : ActivityTransitionAnimator.Listener {
+            override fun onTransitionAnimationStart() {
                 isLaunchingActivity = true
                 activityLaunchProgress = 0f
                 updateAlpha()
             }
 
-            override fun onLaunchAnimationEnd() {
+            override fun onTransitionAnimationEnd() {
                 isLaunchingActivity = false
                 updateAlpha()
             }
 
-            override fun onLaunchAnimationProgress(linearProgress: Float) {
+            override fun onTransitionAnimationProgress(linearProgress: Float) {
                 activityLaunchProgress = linearProgress
                 updateAlpha()
             }
@@ -370,7 +370,7 @@
         updatePauseAuth()
         keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
         lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
-        activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
+        activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener)
         view.startIconAsyncInflate {
             val animationViewInternal: View =
                 view.requireViewById(R.id.udfps_animation_view_internal)
@@ -389,7 +389,7 @@
         if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
             lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
         }
-        activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
+        activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener)
         keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
     }
 
@@ -536,7 +536,7 @@
                 val udfpsActivityLaunchAlphaMultiplier =
                     1f -
                         (activityLaunchProgress *
-                                (ActivityLaunchAnimator.TIMINGS.totalDuration / 83))
+                                (ActivityTransitionAnimator.TIMINGS.totalDuration / 83))
                             .coerceIn(0f, 1f)
                 alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index dc07c1b..0bad33b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -36,6 +37,7 @@
             CommunalWidgetRepositoryModule::class,
             CommunalDatabaseModule::class,
             CommunalPrefsRepositoryModule::class,
+            CommunalSettingsRepositoryModule::class,
         ]
 )
 interface CommunalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
new file mode 100644
index 0000000..83a5bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalEnabledState.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+import java.util.EnumSet
+
+/** Reasons that communal is disabled, primarily for logging. */
+enum class DisabledReason(val loggingString: String) {
+    /** Communal should be disabled due to invalid current user */
+    DISABLED_REASON_INVALID_USER("invalidUser"),
+    /** Communal should be disabled due to the flag being off */
+    DISABLED_REASON_FLAG("flag"),
+    /** Communal should be disabled because the user has turned off the setting */
+    DISABLED_REASON_USER_SETTING("userSetting"),
+    /** Communal is disabled by the device policy app */
+    DISABLED_REASON_DEVICE_POLICY("devicePolicy"),
+}
+
+/**
+ * Model representing the reasons communal hub should be disabled. Allows logging reasons separately
+ * for debugging.
+ */
+@JvmInline
+value class CommunalEnabledState(
+    private val disabledReasons: EnumSet<DisabledReason> =
+        EnumSet.noneOf(DisabledReason::class.java)
+) : Diffable<CommunalEnabledState>, Set<DisabledReason> by disabledReasons {
+
+    /** Creates [CommunalEnabledState] with a single reason for being disabled */
+    constructor(reason: DisabledReason) : this(EnumSet.of(reason))
+
+    /** Checks if there are any reasons communal should be disabled. If none, returns true. */
+    val enabled: Boolean
+        get() = isEmpty()
+
+    override fun logDiffs(prevVal: CommunalEnabledState, row: TableRowLogger) {
+        for (reason in DisabledReason.entries) {
+            val newVal = contains(reason)
+            if (newVal != prevVal.contains(reason)) {
+                row.logChange(
+                    columnName = reason.loggingString,
+                    value = newVal,
+                )
+            }
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        for (reason in DisabledReason.entries) {
+            row.logChange(columnName = reason.loggingString, value = contains(reason))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index addd880..4a06585f5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -16,22 +16,14 @@
 
 package com.android.systemui.communal.data.repository
 
-import com.android.systemui.Flags.communalHub
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.scene.data.repository.SceneContainerRepository
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -39,26 +31,13 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.withContext
 
 /** Encapsulates the state of communal mode. */
 interface CommunalRepository {
-    /** Whether communal features are enabled. */
-    val isCommunalEnabled: Boolean
-
-    /**
-     * A {@link StateFlow} that tracks whether communal hub is enabled (it can be disabled in
-     * settings).
-     */
-    val communalEnabledState: StateFlow<Boolean>
-
     /** Whether the communal hub is showing. */
     val isCommunalHubShowing: Flow<Boolean>
 
@@ -87,37 +66,11 @@
 class CommunalRepositoryImpl
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     @Background backgroundScope: CoroutineScope,
-    @Background private val backgroundDispatcher: CoroutineDispatcher,
-    private val featureFlagsClassic: FeatureFlagsClassic,
     sceneContainerFlags: SceneContainerFlags,
     sceneContainerRepository: SceneContainerRepository,
-    userRepository: UserRepository,
-    private val secureSettings: SecureSettings
 ) : CommunalRepository {
 
-    private val communalEnabledSettingState: Flow<Boolean> =
-        userRepository.selectedUserInfo
-            .flatMapLatest { userInfo -> observeSettings(userInfo.id) }
-            .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed())
-
-    override val communalEnabledState: StateFlow<Boolean> =
-        if (featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()) {
-            communalEnabledSettingState
-                .filterNotNull()
-                .stateIn(
-                    scope = applicationScope,
-                    started = SharingStarted.Eagerly,
-                    initialValue = true
-                )
-        } else {
-            MutableStateFlow(false)
-        }
-
-    override val isCommunalEnabled: Boolean
-        get() = communalEnabledState.value
-
     private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
         MutableStateFlow(CommunalSceneKey.DEFAULT)
     override val desiredScene: StateFlow<CommunalSceneKey> = _desiredScene.asStateFlow()
@@ -153,26 +106,4 @@
         } else {
             desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
         }
-
-    private fun observeSettings(userId: Int): Flow<Boolean> =
-        secureSettings
-            .observerFlow(
-                userId = userId,
-                names =
-                    arrayOf(
-                        GLANCEABLE_HUB_ENABLED,
-                    )
-            )
-            // Force an update
-            .onStart { emit(Unit) }
-            .map { readFromSettings(userId) }
-
-    private suspend fun readFromSettings(userId: Int): Boolean =
-        withContext(backgroundDispatcher) {
-            secureSettings.getIntForUser(GLANCEABLE_HUB_ENABLED, 1, userId) == 1
-        }
-
-    companion object {
-        private const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
new file mode 100644
index 0000000..201b049
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import com.android.systemui.Flags.communalHub
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
+import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import java.util.EnumSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+interface CommunalSettingsRepository {
+    /** A [CommunalEnabledState] for the specified user. */
+    fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+}
+
+@SysUISingleton
+class CommunalSettingsRepositoryImpl
+@Inject
+constructor(
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val featureFlagsClassic: FeatureFlagsClassic,
+    private val secureSettings: SecureSettings,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val devicePolicyManager: DevicePolicyManager,
+) : CommunalSettingsRepository {
+
+    private val flagEnabled: Boolean by lazy {
+        featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
+    }
+
+    override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
+        if (!user.isMain) {
+            return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER))
+        }
+        if (!flagEnabled) {
+            return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG))
+        }
+        return combine(
+                getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING),
+                getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY),
+            ) { reasons ->
+                reasons.filterNotNull()
+            }
+            .map { reasons ->
+                if (reasons.isEmpty()) {
+                    EnumSet.noneOf(DisabledReason::class.java)
+                } else {
+                    EnumSet.copyOf(reasons)
+                }
+            }
+            .map { reasons -> CommunalEnabledState(reasons) }
+            .flowOn(bgDispatcher)
+    }
+
+    private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
+        secureSettings
+            .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
+            // Force an update
+            .onStart { emit(Unit) }
+            .map {
+                secureSettings.getIntForUser(
+                    GLANCEABLE_HUB_ENABLED,
+                    ENABLED_SETTING_DEFAULT,
+                    user.id,
+                ) == 1
+            }
+
+    private fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
+        broadcastDispatcher
+            .broadcastFlow(
+                filter =
+                    IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                user = user.userHandle
+            )
+            .emitOnStart()
+            .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+
+    companion object {
+        const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+        private const val ENABLED_SETTING_DEFAULT = 1
+    }
+}
+
+private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
+    (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
+
+private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled ->
+    if (enabled) null else reason
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
index 128f58b..a931d3f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.animation
+package com.android.systemui.communal.data.repository
 
-import com.android.systemui.kosmos.Kosmos
+import dagger.Binds
+import dagger.Module
 
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+@Module
+interface CommunalSettingsRepositoryModule {
+    @Binds
+    fun communalSettingsRepository(impl: CommunalSettingsRepositoryImpl): CommunalSettingsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 950ac3c..23f590e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.smartspace.data.repository.SmartspaceRepository
-import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.BooleanFlowOperators.and
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.BooleanFlowOperators.or
@@ -74,8 +73,8 @@
     private val communalPrefsRepository: CommunalPrefsRepository,
     mediaRepository: CommunalMediaRepository,
     smartspaceRepository: SmartspaceRepository,
-    userRepository: UserRepository,
     keyguardInteractor: KeyguardInteractor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     private val appWidgetHost: CommunalAppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
     @CommunalLog logBuffer: LogBuffer,
@@ -90,13 +89,12 @@
 
     /** Whether communal features are enabled. */
     val isCommunalEnabled: Boolean
-        get() = communalRepository.isCommunalEnabled
+        get() = communalSettingsInteractor.isCommunalEnabled.value
 
     /** Whether communal features are enabled and available. */
     val isCommunalAvailable: Flow<Boolean> =
         and(
-                communalRepository.communalEnabledState,
-                userRepository.selectedUserInfo.map { it.isMain },
+                communalSettingsInteractor.isCommunalEnabled,
                 not(keyguardInteractor.isEncryptedOrLockdown),
                 or(keyguardInteractor.isKeyguardVisible, keyguardInteractor.isDreaming)
             )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
new file mode 100644
index 0000000..0b096ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.dagger.CommunalTableLog
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalSettingsInteractor
+@Inject
+constructor(
+    @Background private val bgScope: CoroutineScope,
+    private val repository: CommunalSettingsRepository,
+    userInteractor: SelectedUserInteractor,
+    @CommunalTableLog tableLogBuffer: TableLogBuffer,
+) {
+    /** Whether or not communal is enabled for the currently selected user. */
+    val isCommunalEnabled: StateFlow<Boolean> =
+        userInteractor.selectedUserInfo
+            .flatMapLatest { user -> repository.getEnabledState(user) }
+            .logDiffsForTable(
+                tableLogBuffer = tableLogBuffer,
+                columnPrefix = "disabledReason",
+                initialValue = CommunalEnabledState()
+            )
+            .map { model -> model.enabled }
+            // Start this eagerly since the value is accessed synchronously in many places.
+            .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 1404ee2..25dfc02 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -28,17 +28,18 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformWhile
 import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic related to communal tutorial state. */
@@ -51,6 +52,7 @@
     private val communalTutorialRepository: CommunalTutorialRepository,
     keyguardInteractor: KeyguardInteractor,
     private val communalRepository: CommunalRepository,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     communalInteractor: CommunalInteractor,
     @CommunalTableLog tableLogBuffer: TableLogBuffer,
 ) {
@@ -110,20 +112,24 @@
         return null
     }
 
-    private var job: Job? = null
     private fun listenForTransitionToUpdateTutorialState() {
-        if (!communalRepository.isCommunalEnabled) {
-            return
-        }
-        job =
-            scope.launch {
-                tutorialStateToUpdate.collect {
-                    communalTutorialRepository.setTutorialState(it)
-                    if (it == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
-                        job?.cancel()
+        scope.launch {
+            communalSettingsInteractor.isCommunalEnabled
+                .flatMapLatest { enabled ->
+                    if (!enabled) {
+                        emptyFlow()
+                    } else {
+                        tutorialStateToUpdate
                     }
                 }
-            }
+                .transformWhile { tutorialState ->
+                    emit(tutorialState)
+                    tutorialState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+                }
+                .collect { tutorialState ->
+                    communalTutorialRepository.setTutorialState(tutorialState)
+                }
+        }
     }
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index afa7fa9..4c1e77b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -19,7 +19,7 @@
 import android.app.PendingIntent
 import android.view.View
 import android.widget.RemoteViews
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.common.ui.view.getNearestParent
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
@@ -42,7 +42,7 @@
 
     private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean {
         val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
-        val animationController = hostView?.let(ActivityLaunchAnimator.Controller::fromView)
+        val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
 
         activityStarter.startPendingIntentMaybeDismissingKeyguard(
             pendingIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index 9d4ed20..afa2375 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -35,7 +35,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.dagger.ControlsComponent;
@@ -275,8 +275,9 @@
                     .putExtra(ControlsUiController.EXTRA_ANIMATE, true)
                     .putExtra(ControlsUiController.EXIT_TO_DREAM, true);
 
-            final ActivityLaunchAnimator.Controller controller =
-                    v != null ? ActivityLaunchAnimator.Controller.fromView(v, null /* cujType */)
+            final ActivityTransitionAnimator.Controller controller =
+                    v != null
+                            ? ActivityTransitionAnimator.Controller.fromView(v, null /* cujType */)
                             : null;
             if (mControlsComponent.getVisibility() == AVAILABLE) {
                 // Controls can be made visible.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 39bbf07..0ee924d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -130,7 +130,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.TransitionAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -957,8 +957,8 @@
      * Animation launch controller for activities that occlude the keyguard.
      */
     @VisibleForTesting
-    final ActivityLaunchAnimator.Controller mOccludeAnimationController =
-            new ActivityLaunchAnimator.Controller() {
+    final ActivityTransitionAnimator.Controller mOccludeAnimationController =
+            new ActivityTransitionAnimator.Controller() {
                 @Override
                 public void onTransitionAnimationStart(boolean isExpandingFullyAbove) {
                     mOccludeAnimationPlaying = true;
@@ -966,7 +966,8 @@
                 }
 
                 @Override
-                public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
+                public void onTransitionAnimationCancelled(
+                        @Nullable Boolean newKeyguardOccludedState) {
                     Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
                             + mOccluded);
                     mOccludeAnimationPlaying = false;
@@ -1003,7 +1004,7 @@
                 public void setTransitionContainer(@NonNull ViewGroup transitionContainer) {
                     // No-op, launch container is always the shade.
                     Log.wtf(TAG, "Someone tried to change the launch container for the "
-                            + "ActivityLaunchAnimator, which should never happen.");
+                            + "ActivityTransitionAnimator, which should never happen.");
                 }
 
                 @NonNull
@@ -1167,8 +1168,8 @@
 
     /**
      * Animation controller for activities that unocclude the keyguard. This does not use the
-     * ActivityLaunchAnimator since we're just translating down, rather than emerging from a view
-     * or the power button.
+     * ActivityTransitionAnimator since we're just translating down, rather than emerging from a
+     * view or the power button.
      */
     private final IRemoteAnimationRunner mUnoccludeAnimationRunner =
             new IRemoteAnimationRunner.Stub() {
@@ -1347,7 +1348,7 @@
     private boolean mWallpaperSupportsAmbientMode;
     private final KeyguardTransitions mKeyguardTransitions;
 
-    private final Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
+    private final Lazy<ActivityTransitionAnimator> mActivityTransitionAnimator;
     private final Lazy<ScrimController> mScrimControllerLazy;
     private final IActivityTaskManager mActivityTaskManagerService;
 
@@ -1394,7 +1395,7 @@
             WallpaperRepository wallpaperRepository,
             Lazy<ShadeController> shadeControllerLazy,
             Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
-            Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+            Lazy<ActivityTransitionAnimator> activityTransitionAnimator,
             Lazy<ScrimController> scrimControllerLazy,
             IActivityTaskManager activityTaskManagerService,
             FeatureFlags featureFlags,
@@ -1459,7 +1460,7 @@
         mJavaAdapter = javaAdapter;
         mWallpaperRepository = wallpaperRepository;
 
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mActivityTransitionAnimator = activityTransitionAnimator;
         mScrimControllerLazy = scrimControllerLazy;
         mActivityTaskManagerService = activityTaskManagerService;
 
@@ -3812,15 +3813,15 @@
 
     /**
      * Implementation of RemoteAnimationRunner that creates a new
-     * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the
+     * {@link ActivityTransitionAnimator.Runner} whenever onAnimationStart is called, delegating the
      * remote animation methods to that runner.
      */
     private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub {
 
-        private final ActivityLaunchAnimator.Controller mActivityLaunchController;
-        @Nullable private ActivityLaunchAnimator.Runner mRunner;
+        private final ActivityTransitionAnimator.Controller mActivityLaunchController;
+        @Nullable private ActivityTransitionAnimator.Runner mRunner;
 
-        ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) {
+        ActivityLaunchRemoteAnimationRunner(ActivityTransitionAnimator.Controller controller) {
             mActivityLaunchController = controller;
         }
 
@@ -3837,7 +3838,7 @@
                 RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback)
                 throws RemoteException {
-            mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController);
+            mRunner = mActivityTransitionAnimator.get().createRunner(mActivityLaunchController);
             mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
         }
     }
@@ -3850,7 +3851,7 @@
             extends ActivityLaunchRemoteAnimationRunner {
 
         OccludeActivityLaunchRemoteAnimationRunner(
-                ActivityLaunchAnimator.Controller controller) {
+                ActivityTransitionAnimator.Controller controller) {
             super(controller);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 70da3e7..0b227fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -35,7 +35,7 @@
 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingModule;
@@ -150,7 +150,7 @@
             WallpaperRepository wallpaperRepository,
             Lazy<ShadeController> shadeController,
             Lazy<NotificationShadeWindowController> notificationShadeWindowController,
-            Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+            Lazy<ActivityTransitionAnimator> activityTransitionAnimator,
             Lazy<ScrimController> scrimControllerLazy,
             IActivityTaskManager activityTaskManagerService,
             FeatureFlags featureFlags,
@@ -196,7 +196,7 @@
                 wallpaperRepository,
                 shadeController,
                 notificationShadeWindowController,
-                activityLaunchAnimator,
+                activityTransitionAnimator,
                 scrimControllerLazy,
                 activityTaskManagerService,
                 featureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 96e83b0..3630b40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -32,7 +32,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
 import com.android.settingslib.Utils
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.view.LaunchableLinearLayout
 import com.android.systemui.common.shared.model.Icon
@@ -534,7 +534,7 @@
         activityStarter.postStartActivityDismissingKeyguard(
             WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
             /* delay= */ 0,
-            /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
+            /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
             /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index f67cb68..b1adef4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -22,7 +22,7 @@
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.common.ui.binder.TextViewBinder
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
@@ -115,7 +115,7 @@
         activityStarter.postStartActivityDismissingKeyguard(
             WallpaperPickerIntentUtils.getIntent(view.context, LAUNCH_SOURCE_KEYGUARD),
             /* delay= */ 0,
-            /* animationController= */ ActivityLaunchAnimator.Controller.fromView(view),
+            /* animationController= */ ActivityTransitionAnimator.Controller.fromView(view),
             /* customMessage= */ view.context.getString(R.string.keyguard_unlock_to_customize_ls)
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index 9cf3c95..d4ea728 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -28,6 +28,7 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
 
 /** Models UI state for the alpha of the AOD (always-on display). */
 @SysUISingleton
@@ -42,13 +43,15 @@
     /** The alpha level for the entire lockscreen while in AOD. */
     val alpha: Flow<Float> =
         combine(
-                keyguardTransitionInteractor.currentKeyguardState,
+                keyguardTransitionInteractor.transitionValue(KeyguardState.GONE).onStart {
+                    emit(0f)
+                },
                 merge(
                     keyguardInteractor.keyguardAlpha,
                     occludedToLockscreenTransitionViewModel.lockscreenAlpha,
                 )
-            ) { currentKeyguardState, alpha ->
-                if (currentKeyguardState == KeyguardState.GONE) {
+            ) { transitionToGone, alpha ->
+                if (transitionToGone == 1f) {
                     // Ensures content is not visible when in GONE state
                     0f
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 2b28a71..fa18557 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@
 constructor(
     @Application applicationScope: CoroutineScope,
     deviceEntryInteractor: DeviceEntryInteractor,
-    communalInteractor: CommunalInteractor,
+    communalSettingsInteractor: CommunalSettingsInteractor,
     val longPress: KeyguardLongPressViewModel,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
@@ -55,10 +55,12 @@
     }
 
     /** The key of the scene we should switch to when swiping left. */
-    val leftDestinationSceneKey: SceneKey? =
-        if (communalInteractor.isCommunalEnabled) {
-            SceneKey.Communal
-        } else {
-            null
-        }
+    val leftDestinationSceneKey: StateFlow<SceneKey?> =
+        communalSettingsInteractor.isCommunalEnabled
+            .map { available -> if (available) SceneKey.Communal else null }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = null,
+            )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/OWNERS b/packages/SystemUI/src/com/android/systemui/media/OWNERS
index b2d00df..69ea57b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/media/OWNERS
@@ -1,5 +1 @@
 per-file MediaProjectionPermissionActivity.java = michaelwr@google.com
-
-# Haptics team also works on Ringtone
-per-file NotificationPlayer.java = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file RingtonePlayer.java = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 5720cc7..eae7789 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -81,7 +81,7 @@
 import com.android.internal.widget.CachingIconView;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
 import com.android.systemui.bluetooth.BroadcastDialogController;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -1309,7 +1309,7 @@
     }
 
     @Nullable
-    private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
+    private ActivityTransitionAnimator.Controller buildLaunchAnimatorController(
             TransitionLayout player) {
         if (!(player.getParent() instanceof ViewGroup)) {
             // TODO(b/192194319): Throw instead of just logging.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 523414c..35e0271 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -1155,7 +1155,7 @@
                 onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
                 // TODO(b/311234666): revisit logic once interactions between the hub and
                 //  shade/keyguard state are finalized
-                isCommunalShowing && communalInteractor.isCommunalEnabled -> LOCATION_COMMUNAL_HUB
+                isCommunalShowing -> LOCATION_COMMUNAL_HUB
                 onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
                 else -> LOCATION_QQS
             }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 375a0ce..687f268 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -78,7 +78,7 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
@@ -400,7 +400,7 @@
     void tryToLaunchInAppRoutingIntent(String routeId, View view) {
         ComponentName componentName = mLocalMediaManager.getLinkedItemComponentName();
         if (componentName != null) {
-            ActivityLaunchAnimator.Controller controller =
+            ActivityTransitionAnimator.Controller controller =
                     mDialogLaunchAnimator.createActivityLaunchController(view);
             Intent launchIntent = new Intent(ACTION_TRANSFER_MEDIA);
             launchIntent.setComponent(componentName);
@@ -412,7 +412,7 @@
     }
 
     void tryToLaunchMediaApplication(View view) {
-        ActivityLaunchAnimator.Controller controller =
+        ActivityTransitionAnimator.Controller controller =
                 mDialogLaunchAnimator.createActivityLaunchController(view);
         Intent launchIntent = getAppLaunchIntent();
         if (launchIntent != null) {
@@ -881,7 +881,7 @@
     }
 
     void launchBluetoothPairing(View view) {
-        ActivityLaunchAnimator.Controller controller =
+        ActivityTransitionAnimator.Controller controller =
                 mDialogLaunchAnimator.createActivityLaunchController(view);
 
         if (controller == null || (mKeyGuardManager != null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 0941a20..3fd9803 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -11,7 +11,7 @@
 import androidx.annotation.WorkerThread
 import com.android.internal.R
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.appops.AppOpsController
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.FeatureFlags
@@ -175,7 +175,7 @@
             startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK
             uiExecutor.execute {
                 activityStarter.startActivity(startSafetyCenter, true,
-                    ActivityLaunchAnimator.Controller.fromView(privacyChip))
+                    ActivityTransitionAnimator.Controller.fromView(privacyChip))
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index bd13d06..b53c245 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,7 +51,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -65,14 +65,14 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.DisplayTracker;
 
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import dagger.Lazy;
 import dagger.assisted.Assisted;
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 public class CustomTile extends QSTileImpl<State> implements TileChangeListener,
         CustomTileInterface {
     public static final String PREFIX = "custom(";
@@ -540,9 +540,9 @@
         } else {
             Log.i(TAG, "The activity is starting");
 
-            ActivityLaunchAnimator.Controller controller =
+            ActivityTransitionAnimator.Controller controller =
                     mViewClicked == null ? null :
-                    ActivityLaunchAnimator.Controller.fromView(
+                    ActivityTransitionAnimator.Controller.fromView(
                             mViewClicked,
                             InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
                     );
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 529d684..35cac4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -57,7 +57,7 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
@@ -70,7 +70,6 @@
 import com.android.systemui.qs.logging.QSLogger;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 /**
  * Base quick-settings tile, extend this to create a new tile.
@@ -412,8 +411,8 @@
      * @param view The view from which the opening window will be animated.
      */
     protected void handleLongClick(@Nullable View view) {
-        ActivityLaunchAnimator.Controller animationController =
-                view != null ? ActivityLaunchAnimator.Controller.fromView(view,
+        ActivityTransitionAnimator.Controller animationController =
+                view != null ? ActivityTransitionAnimator.Controller.fromView(view,
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
         mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
                 animationController);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index d98141f..688f3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -14,7 +14,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -75,7 +75,7 @@
 
     override fun handleClick(view: View?) {
         val animationController = view?.let {
-            ActivityLaunchAnimator.Controller.fromView(
+            ActivityTransitionAnimator.Controller.fromView(
                     it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
         }
         val pendingIntent = lastAlarmInfo?.showIntent
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a698208..690b711 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -38,7 +38,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -222,7 +222,7 @@
                     mContext,
                     ROUTE_TYPE_REMOTE_DISPLAY,
                     v -> {
-                        ActivityLaunchAnimator.Controller controller =
+                        ActivityTransitionAnimator.Controller controller =
                                 mDialogLaunchAnimator.createActivityLaunchController(v);
 
                         if (controller == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 91b2d97..bb175e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -26,7 +26,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
@@ -112,7 +112,7 @@
             putExtra(ControlsUiController.EXTRA_ANIMATE, true)
         }
         val animationController = view?.let {
-            ActivityLaunchAnimator.Controller.fromView(
+            ActivityTransitionAnimator.Controller.fromView(
                     it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index f70e27d..de9a08e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -27,8 +27,7 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -40,6 +39,7 @@
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
 
 import javax.inject.Inject;
 
@@ -108,8 +108,8 @@
             return;
         }
 
-        ActivityLaunchAnimator.Controller animationController =
-                view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+        ActivityTransitionAnimator.Controller animationController =
+                view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
         mActivityStarter.startActivity(intent, true /* dismissShade */,
                 animationController, true /* showOverLockscreenWhenLocked */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 3b8fb26..1b73225 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -43,7 +43,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
@@ -132,8 +132,8 @@
 
     @Override
     protected void handleClick(@Nullable View view) {
-        ActivityLaunchAnimator.Controller animationController =
-                view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+        ActivityTransitionAnimator.Controller animationController =
+                view == null ? null : ActivityTransitionAnimator.Controller.fromView(view,
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
 
         mUiHandler.post(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index fe10eaa..7192f58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -22,7 +22,7 @@
 import android.os.UserHandle
 import android.view.View
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
@@ -53,9 +53,9 @@
 ) : QSTileIntentUserInputHandler {
 
     override fun handle(view: View?, intent: Intent) {
-        val animationController: ActivityLaunchAnimator.Controller? =
+        val animationController: ActivityTransitionAnimator.Controller? =
             view?.let {
-                ActivityLaunchAnimator.Controller.fromView(
+                ActivityTransitionAnimator.Controller.fromView(
                     it,
                     InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
                 )
@@ -70,9 +70,9 @@
         requestLaunchingDefaultActivity: Boolean
     ) {
         if (pendingIntent.isActivity) {
-            val animationController: ActivityLaunchAnimator.Controller? =
+            val animationController: ActivityTransitionAnimator.Controller? =
                 view?.let {
-                    ActivityLaunchAnimator.Controller.fromView(
+                    ActivityTransitionAnimator.Controller.fromView(
                         it,
                         InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
                     )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 211b4594..41de65c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,7 +75,7 @@
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.settingslib.wifi.WifiUtils;
 import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -748,7 +748,7 @@
     }
 
     private void startActivity(Intent intent, View view) {
-        ActivityLaunchAnimator.Controller controller =
+        ActivityTransitionAnimator.Controller controller =
                 mDialogLaunchAnimator.createActivityLaunchController(view);
 
         if (controller == null && mCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 1c7cc00..df845f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.util.kotlin.collectFlow
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 /**
  * Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -105,13 +106,9 @@
      */
     private var shadeShowing = false
 
-    /** Returns true if the glanceable hub is enabled and the container view can be created. */
-    fun isEnabled(): Boolean {
-        return communalInteractor.isCommunalEnabled && isComposeAvailable()
-    }
-
-    /** Returns a {@link StateFlow} that tracks whether communal hub is available. */
-    fun communalAvailable(): Flow<Boolean> = communalInteractor.isCommunalAvailable
+    /** Returns a flow that tracks whether communal hub is available. */
+    fun communalAvailable(): Flow<Boolean> =
+        if (isComposeAvailable()) communalInteractor.isCommunalAvailable else flowOf(false)
 
     /**
      * Creates the container view containing the glanceable hub UI.
@@ -127,9 +124,6 @@
     /** Override for testing. */
     @VisibleForTesting
     internal fun initView(containerView: View): View {
-        if (!isEnabled()) {
-            throw RuntimeException("Glanceable hub is not enabled")
-        }
         if (communalContainerView != null) {
             throw RuntimeException("Communal view has already been initialized")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 73d229e..353e143 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -112,7 +112,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.TransitionAnimator;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -259,7 +259,7 @@
     /** The parallax amount of the quick settings translation when dragging down the panel. */
     public static final float QS_PARALLAX_AMOUNT = 0.175f;
     private static final long ANIMATION_DELAY_ICON_FADE_IN =
-            ActivityLaunchAnimator.TIMINGS.getTotalDuration()
+            ActivityTransitionAnimator.TIMINGS.getTotalDuration()
                     - CollapsedStatusBarFragment.FADE_IN_DURATION
                     - CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
     private static final int NO_FIXED_DURATION = -1;
@@ -3231,7 +3231,7 @@
 
     @Override
     public void applyLaunchAnimationProgress(float linearProgress) {
-        boolean hideIcons = TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
+        boolean hideIcons = TransitionAnimator.getProgress(ActivityTransitionAnimator.TIMINGS,
                 linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
         if (hideIcons != mHideIconsDuringLaunchAnimation) {
             mHideIconsDuringLaunchAnimation = hideIcons;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index ef820f3..aa2d606 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -34,7 +34,7 @@
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.keyguard.LockIconViewController;
 import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
@@ -679,7 +679,7 @@
     void setExpandAnimationRunning(boolean running) {
         if (mExpandAnimationRunning != running) {
             // TODO(b/288507023): Remove this log.
-            if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+            if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
                 Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
             }
             if (running) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index b64e0b7..91340be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -26,7 +26,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.AnimationFeatureFlags;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -190,8 +190,8 @@
     /** */
     @Provides
     @SysUISingleton
-    static ActivityLaunchAnimator provideActivityLaunchAnimator() {
-        return new ActivityLaunchAnimator();
+    static ActivityTransitionAnimator provideActivityTransitionAnimator() {
+        return new ActivityTransitionAnimator();
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
index 6f4a7cd..4af8cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
@@ -2,7 +2,7 @@
 
 import android.util.MathUtils
 import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.app.animation.Interpolators
 import com.android.systemui.animation.TransitionAnimator
 import kotlin.math.min
@@ -58,7 +58,11 @@
         }
 
     fun getProgress(delay: Long, duration: Long): Float {
-        return TransitionAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, linearProgress, delay,
-            duration)
+        return TransitionAnimator.getProgress(
+            ActivityTransitionAnimator.TIMINGS,
+            linearProgress,
+            delay,
+            duration
+        )
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 8fc9619..02d1e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -19,7 +19,7 @@
 import android.util.Log
 import android.view.ViewGroup
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.TransitionAnimator
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -55,9 +55,9 @@
 }
 
 /**
- * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
- * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
- * notification expanding into an opening window.
+ * An [ActivityTransitionAnimator.Controller] that animates an [ExpandableNotificationRow]. An
+ * instance of this class can be passed to [ActivityTransitionAnimator.startIntentWithAnimation] to
+ * animate a notification expanding into an opening window.
  */
 class NotificationLaunchAnimatorController(
     private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
@@ -66,7 +66,7 @@
     private val notification: ExpandableNotificationRow,
     private val jankMonitor: InteractionJankMonitor,
     private val onFinishAnimationCallback: Runnable?
-) : ActivityLaunchAnimator.Controller {
+) : ActivityTransitionAnimator.Controller {
 
     companion object {
         const val ANIMATION_DURATION_TOP_ROUNDING = 100L
@@ -140,7 +140,7 @@
     }
 
     override fun onIntentStarted(willAnimate: Boolean) {
-        if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+        if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
             Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
         }
         notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate)
@@ -173,8 +173,8 @@
         headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
     }
 
-    override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
-        if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+    override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+        if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
             Log.d(TAG, "onLaunchAnimationCancelled()")
         }
 
@@ -194,7 +194,7 @@
     }
 
     override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
-        if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+        if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
             Log.d(TAG, "onLaunchAnimationEnd()")
         }
         jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
index 22ce4f1..a3189a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationLaunchAnimationInteractor.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.notification.domain.interactor
 
 import android.util.Log
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
 import javax.inject.Inject
@@ -40,7 +40,7 @@
 
     /** Sets whether the notification expansion launch animation is currently running. */
     fun setIsLaunchAnimationRunning(running: Boolean) {
-        if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
+        if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
             Log.d(TAG, "setIsLaunchAnimationRunning(running=$running)")
         }
         repository.isLaunchAnimationRunning.value = running
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index aca8b64..d7fe36f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -223,7 +223,8 @@
             // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
             // info, and NotificationLockscreenUserManagerImpl is already listening for updates
             // to those
-            entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
+            entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
+                    VISIBILITY_SECRET
         } else {
             entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9312dba..ebe7fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -233,7 +233,7 @@
     private final ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
     private final ArrayList<View> mSwipedOutViews = new ArrayList<>();
     private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
-    private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
+    private final StackStateAnimator mStateAnimator;
     private boolean mAnimationsEnabled;
     private boolean mChangePositionInProgress;
     private boolean mChildTransferInProgress;
@@ -670,6 +670,7 @@
         mExpandHelper.setScrollAdapter(mScrollAdapter);
 
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
+        mStateAnimator = new StackStateAnimator(context, this);
         mShouldDrawNotificationBackground =
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         setOutlineProvider(mOutlineProvider);
@@ -1083,6 +1084,7 @@
         }
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
         mStackScrollAlgorithm.initView(context);
+        mStateAnimator.initView(context);
         mAmbientState.reload(context);
         mPaddingBetweenElements = Math.max(1,
                 res.getDimensionPixelSize(R.dimen.notification_divider_height));
@@ -4363,6 +4365,12 @@
                     layoutEnd -= mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
                 }
                 if (endPosition > layoutEnd) {
+                    // if Scene Container is active, send bottom notification expansion delta
+                    // to it so that it can scroll the stack and scrim accordingly.
+                    if (SceneContainerFlag.isEnabled()) {
+                        float diff = endPosition - layoutEnd;
+                        mController.sendSyntheticScrollToSceneFramework(diff);
+                    }
                     setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
                     mDisallowScrollingInThisMotion = true;
                 }
@@ -5081,6 +5089,10 @@
     }
 
     private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
+        // If scene container is active, NSSL should not control its own scrolling.
+        if (SceneContainerFlag.isEnabled()) {
+            return;
+        }
         // Avoid Flicking during clear all
         // when the shade finishes closing, onExpansionStopped will call
         // resetScrollPosition to setOwnScrollY to 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c744a43..5fa0624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1151,6 +1151,11 @@
         }
     }
 
+    /** Send internal notification expansion to the scene container framework. */
+    public void sendSyntheticScrollToSceneFramework(Float delta) {
+        mStackAppearanceInteractor.setSyntheticScroll(delta);
+    }
+
     /** Get the y-coordinate of the top bound of the stack. */
     public float getPlaceholderTop() {
         return mStackAppearanceInteractor.getStackBounds().getValue().getTop();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index b38d619..ab62ed6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -23,6 +23,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.content.Context;
 import android.util.Property;
 import android.view.View;
 
@@ -66,9 +67,8 @@
     public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
     private static final int MAX_STAGGER_COUNT = 5;
 
-    private final int mGoToFullShadeAppearingTranslation;
-    @VisibleForTesting
-    float mHeadsUpAppearStartAboveScreen;
+    @VisibleForTesting int mGoToFullShadeAppearingTranslation;
+    @VisibleForTesting float mHeadsUpAppearStartAboveScreen;
     private final ExpandableViewState mTmpState = new ExpandableViewState();
     private final AnimationProperties mAnimationProperties;
     public NotificationStackScrollLayout mHostLayout;
@@ -92,14 +92,9 @@
     private NotificationShelf mShelf;
     private StackStateLogger mLogger;
 
-    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
+    public StackStateAnimator(Context context, NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
-        // TODO(b/317061579) reload on configuration changes
-        mGoToFullShadeAppearingTranslation =
-                hostLayout.getContext().getResources().getDimensionPixelSize(
-                        R.dimen.go_to_full_shade_appearing_translation);
-        mHeadsUpAppearStartAboveScreen = hostLayout.getContext().getResources()
-                .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen);
+        initView(context);
         mAnimationProperties = new AnimationProperties() {
             @Override
             public AnimationFilter getAnimationFilter() {
@@ -118,6 +113,21 @@
         };
     }
 
+    /**
+     * Needs to be called on configuration changes, to update cached resource values.
+     */
+    public void initView(Context context) {
+        updateResources(context);
+    }
+
+    private void updateResources(Context context) {
+        mGoToFullShadeAppearingTranslation =
+                context.getResources().getDimensionPixelSize(
+                        R.dimen.go_to_full_shade_appearing_translation);
+        mHeadsUpAppearStartAboveScreen = context.getResources()
+                .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen);
+    }
+
     protected void setLogger(StackStateLogger logger) {
         mLogger = logger;
     }
@@ -460,15 +470,8 @@
                 mHeadsUpAppearChildren.add(changingView);
 
                 mTmpState.copyFrom(changingView.getViewState());
-                if (event.headsUpFromBottom) {
-                    // start from the bottom of the screen
-                    mTmpState.setYTranslation(
-                            mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
-                } else {
-                    // start from the top of the screen
-                    mTmpState.setYTranslation(
-                            -mStackTopMargin - mHeadsUpAppearStartAboveScreen);
-                }
+                // translate the HUN in from the top, or the bottom of the screen
+                mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
                 // set the height and the initial position
                 mTmpState.applyToView(changingView);
                 mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
@@ -512,12 +515,20 @@
                     || event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
                 mHeadsUpDisappearChildren.add(changingView);
                 Runnable endRunnable = null;
+                mTmpState.copyFrom(changingView.getViewState());
                 if (changingView.getParent() == null) {
                     // This notification was actually removed, so we need to add it
                     // transiently
                     mHostLayout.addTransientView(changingView, 0);
                     changingView.setTransientContainer(mHostLayout);
-                    mTmpState.initFrom(changingView);
+                    if (NotificationsImprovedHunAnimation.isEnabled()) {
+                        // StackScrollAlgorithm cannot find this view because it has been removed
+                        // from the NSSL. To correctly translate the view to the top or bottom of
+                        // the screen (where it animated from), we need to update its translation.
+                        mTmpState.setYTranslation(
+                                getHeadsUpYTranslationStart(event.headsUpFromBottom)
+                        );
+                    }
                     endRunnable = changingView::removeFromTransientContainer;
                 }
 
@@ -565,16 +576,19 @@
                             changingView.setInRemovalAnimation(true);
                         };
                     }
-                    if (NotificationsImprovedHunAnimation.isEnabled()) {
-                        mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
-                                Interpolators.FAST_OUT_SLOW_IN_REVERSE);
-                    }
                     long removeAnimationDelay = changingView.performRemoveAnimation(
                             ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
                             0, 0.0f, true /* isHeadsUpAppear */,
                             startAnimation, postAnimation,
                             getGlobalAnimationFinishedListener());
                     mAnimationProperties.delay += removeAnimationDelay;
+                    if (NotificationsImprovedHunAnimation.isEnabled()) {
+                        mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
+                        mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
+                                Interpolators.FAST_OUT_SLOW_IN_REVERSE);
+                        mAnimationProperties.getAnimationFilter().animateY = true;
+                        mTmpState.animateTo(changingView, mAnimationProperties);
+                    }
                 } else if (endRunnable != null) {
                     endRunnable.run();
                 }
@@ -585,6 +599,15 @@
         return needsCustomAnimation;
     }
 
+    private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) {
+        if (headsUpFromBottom) {
+            // start from the bottom of the screen
+            return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen;
+        }
+        // start from the top of the screen
+        return -mStackTopMargin - mHeadsUpAppearStartAboveScreen;
+    }
+
     public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
             final boolean isRubberbanded) {
         final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
index 311ba83..9efe632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -47,4 +47,11 @@
      * further.
      */
     val scrolledToTop = MutableStateFlow(true)
+
+    /**
+     * The amount in px that the notification stack should scroll due to internal expansion. This
+     * should only happen when a notification expansion hits the bottom of the screen, so it is
+     * necessary to scroll up to keep expanding the notification.
+     */
+    val syntheticScroll = MutableStateFlow(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 9984ba9..08df473 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.stack.data.repository.NotificationStackAppearanceRepository
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
@@ -50,6 +51,13 @@
      */
     val scrolledToTop: StateFlow<Boolean> = repository.scrolledToTop.asStateFlow()
 
+    /**
+     * The amount in px that the notification stack should scroll due to internal expansion. This
+     * should only happen when a notification expansion hits the bottom of the screen, so it is
+     * necessary to scroll up to keep expanding the notification.
+     */
+    val syntheticScroll: Flow<Float> = repository.syntheticScroll.asStateFlow()
+
     /** Sets the position of the notification stack in the current scene. */
     fun setStackBounds(bounds: NotificationContainerBounds) {
         check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
@@ -70,4 +78,9 @@
     fun setScrolledToTop(scrolledToTop: Boolean) {
         repository.scrolledToTop.value = scrolledToTop
     }
+
+    /** Sets the amount (px) that the notification stack should scroll due to internal expansion. */
+    fun setSyntheticScroll(delta: Float) {
+        repository.syntheticScroll.value = delta
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index 814146c..a157785 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -69,6 +69,9 @@
                         controller.setMaxAlphaForExpansion(
                             ((expandFraction - 0.5f) / 0.5f).coerceAtLeast(0f)
                         )
+                        if (expandFraction == 0f || expandFraction == 1f) {
+                            controller.onExpansionStopped()
+                        }
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index bdf1a64..3a0f03f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -28,6 +28,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 
 /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
 @SysUISingleton
@@ -45,31 +46,32 @@
      */
     val expandFraction: Flow<Float> =
         combine(
-            shadeInteractor.shadeExpansion,
-            sceneInteractor.transitionState,
-        ) { shadeExpansion, transitionState ->
-            when (transitionState) {
-                is ObservableTransitionState.Idle -> {
-                    if (transitionState.scene == SceneKey.Lockscreen) {
-                        1f
-                    } else {
-                        shadeExpansion
+                shadeInteractor.shadeExpansion,
+                sceneInteractor.transitionState,
+            ) { shadeExpansion, transitionState ->
+                when (transitionState) {
+                    is ObservableTransitionState.Idle -> {
+                        if (transitionState.scene == SceneKey.Lockscreen) {
+                            1f
+                        } else {
+                            shadeExpansion
+                        }
                     }
-                }
-                is ObservableTransitionState.Transition -> {
-                    if (
-                        (transitionState.fromScene == SceneKey.Shade &&
-                            transitionState.toScene == SceneKey.QuickSettings) ||
-                            (transitionState.fromScene == SceneKey.QuickSettings &&
-                                transitionState.toScene == SceneKey.Shade)
-                    ) {
-                        1f
-                    } else {
-                        shadeExpansion
+                    is ObservableTransitionState.Transition -> {
+                        if (
+                            (transitionState.fromScene == SceneKey.Shade &&
+                                transitionState.toScene == SceneKey.QuickSettings) ||
+                                (transitionState.fromScene == SceneKey.QuickSettings &&
+                                    transitionState.toScene == SceneKey.Shade)
+                        ) {
+                            1f
+                        } else {
+                            shadeExpansion
+                        }
                     }
                 }
             }
-        }
+            .distinctUntilChanged()
 
     /** The bounds of the notification stack in the current scene. */
     val stackBounds: Flow<NotificationContainerBounds> = stackAppearanceInteractor.stackBounds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 65d9c9f..7ac5cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -86,6 +86,13 @@
      */
     val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
 
+    /**
+     * The amount in px that the notification stack should scroll due to internal expansion. This
+     * should only happen when a notification expansion hits the bottom of the screen, so it is
+     * necessary to scroll up to keep expanding the notification.
+     */
+    val syntheticScroll: Flow<Float> = interactor.syntheticScroll
+
     /** Sets the y-coord in px of the top of the contents of the notification stack. */
     fun onContentTopChanged(padding: Float) {
         interactor.setContentTop(padding)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index b49af0e..80d45fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -30,8 +30,8 @@
 import android.view.WindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator.PendingIntentStarter
 import com.android.systemui.animation.DelegateLaunchAnimatorController
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent
@@ -75,7 +75,7 @@
     private val shadeAnimationInteractor: ShadeAnimationInteractor,
     private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>,
     private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
-    private val activityLaunchAnimator: ActivityLaunchAnimator,
+    private val activityTransitionAnimator: ActivityTransitionAnimator,
     private val context: Context,
     @DisplayId private val displayId: Int,
     private val lockScreenUserManager: NotificationLockscreenUserManager,
@@ -127,7 +127,7 @@
     override fun startPendingIntentDismissingKeyguard(
         intent: PendingIntent,
         intentSentUiThreadCallback: Runnable?,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
     ) {
         activityStarterInternal.startPendingIntentDismissingKeyguard(
             intent = intent,
@@ -139,7 +139,7 @@
     override fun startPendingIntentMaybeDismissingKeyguard(
         intent: PendingIntent,
         intentSentUiThreadCallback: Runnable?,
-        animationController: ActivityLaunchAnimator.Controller?
+        animationController: ActivityTransitionAnimator.Controller?
     ) {
         activityStarterInternal.startPendingIntentDismissingKeyguard(
             intent = intent,
@@ -209,7 +209,7 @@
     override fun startActivity(
         intent: Intent,
         dismissShade: Boolean,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
         showOverLockscreenWhenLocked: Boolean,
     ) {
         activityStarterInternal.startActivity(
@@ -222,7 +222,7 @@
     override fun startActivity(
         intent: Intent,
         dismissShade: Boolean,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
         showOverLockscreenWhenLocked: Boolean,
         userHandle: UserHandle?,
     ) {
@@ -245,7 +245,7 @@
 
     override fun postStartActivityDismissingKeyguard(
         intent: PendingIntent,
-        animationController: ActivityLaunchAnimator.Controller?
+        animationController: ActivityTransitionAnimator.Controller?
     ) {
         postOnUiThread {
             activityStarterInternal.startPendingIntentDismissingKeyguard(
@@ -268,7 +268,7 @@
     override fun postStartActivityDismissingKeyguard(
         intent: Intent,
         delay: Int,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
     ) {
         postOnUiThread(delay) {
             activityStarterInternal.startActivityDismissingKeyguard(
@@ -283,7 +283,7 @@
     override fun postStartActivityDismissingKeyguard(
         intent: Intent,
         delay: Int,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
         customMessage: String?,
     ) {
         postOnUiThread(delay) {
@@ -342,7 +342,7 @@
         disallowEnterPictureInPictureWhileLaunching: Boolean,
         callback: ActivityStarter.Callback?,
         flags: Int,
-        animationController: ActivityLaunchAnimator.Controller?,
+        animationController: ActivityTransitionAnimator.Controller?,
         userHandle: UserHandle?,
     ) {
         activityStarterInternal.startActivityDismissingKeyguard(
@@ -430,7 +430,7 @@
             disallowEnterPictureInPictureWhileLaunching: Boolean = false,
             callback: ActivityStarter.Callback? = null,
             flags: Int = 0,
-            animationController: ActivityLaunchAnimator.Controller? = null,
+            animationController: ActivityTransitionAnimator.Controller? = null,
             userHandle: UserHandle? = null,
             customMessage: String? = null,
         ) {
@@ -464,7 +464,7 @@
                 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                 intent.addFlags(flags)
                 val result = intArrayOf(ActivityManager.START_CANCELED)
-                activityLaunchAnimator.startIntentWithAnimation(
+                activityTransitionAnimator.startIntentWithAnimation(
                     animController,
                     animate,
                     intent.getPackage()
@@ -552,7 +552,7 @@
             intent: PendingIntent,
             intentSentUiThreadCallback: Runnable? = null,
             associatedView: View? = null,
-            animationController: ActivityLaunchAnimator.Controller? = null,
+            animationController: ActivityTransitionAnimator.Controller? = null,
             showOverLockscreen: Boolean = false,
         ) {
             val animationController =
@@ -602,7 +602,7 @@
             val collapse = !animate
             val runnable = Runnable {
                 try {
-                    activityLaunchAnimator.startPendingIntentWithAnimation(
+                    activityTransitionAnimator.startPendingIntentWithAnimation(
                         controller,
                         animate,
                         intent.creatorPackage,
@@ -670,7 +670,7 @@
         fun startActivity(
             intent: Intent,
             dismissShade: Boolean = false,
-            animationController: ActivityLaunchAnimator.Controller? = null,
+            animationController: ActivityTransitionAnimator.Controller? = null,
             showOverLockscreenWhenLocked: Boolean = false,
             userHandle: UserHandle? = null,
         ) {
@@ -698,7 +698,7 @@
                         showOverLockscreenWhenLocked
                     ) == true
 
-            var controller: ActivityLaunchAnimator.Controller? = null
+            var controller: ActivityTransitionAnimator.Controller? = null
             if (animate) {
                 // Wrap the animation controller to dismiss the shade and set
                 // mIsLaunchingActivityOverLockscreen during the animation.
@@ -721,7 +721,7 @@
                 centralSurfaces?.awakenDreams()
             }
 
-            activityLaunchAnimator.startIntentWithAnimation(
+            activityTransitionAnimator.startIntentWithAnimation(
                 controller,
                 animate,
                 intent.getPackage(),
@@ -815,7 +815,7 @@
         }
 
         /**
-         * Return a [ActivityLaunchAnimator.Controller] wrapping `animationController` so that:
+         * Return a [ActivityTransitionAnimator.Controller] wrapping `animationController` so that:
          * - if it launches in the notification shade window and `dismissShade` is true, then the
          *   shade will be instantly dismissed at the end of the animation.
          * - if it launches in status bar window, it will make the status bar window match the
@@ -830,15 +830,15 @@
          * @param isLaunchForActivity whether the launch is for an activity.
          */
         private fun wrapAnimationControllerForShadeOrStatusBar(
-            animationController: ActivityLaunchAnimator.Controller?,
+            animationController: ActivityTransitionAnimator.Controller?,
             dismissShade: Boolean,
             isLaunchForActivity: Boolean,
-        ): ActivityLaunchAnimator.Controller? {
+        ): ActivityTransitionAnimator.Controller? {
             if (animationController == null) {
                 return null
             }
             val rootView = animationController.transitionContainer.rootView
-            val controllerFromStatusBar: Optional<ActivityLaunchAnimator.Controller> =
+            val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> =
                 statusBarWindowController.wrapAnimationControllerIfInStatusBar(
                     rootView,
                     animationController
@@ -870,8 +870,8 @@
          * lockscreen, the correct flags are set for it to be occluded.
          */
         private fun wrapAnimationControllerForLockscreen(
-            animationController: ActivityLaunchAnimator.Controller?
-        ): ActivityLaunchAnimator.Controller? {
+            animationController: ActivityTransitionAnimator.Controller?
+        ): ActivityTransitionAnimator.Controller? {
             return animationController?.let {
                 object : DelegateLaunchAnimatorController(it) {
                     override fun onIntentStarted(willAnimate: Boolean) {
@@ -912,7 +912,9 @@
                         delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
                     }
 
-                    override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+                    override fun onTransitionAnimationCancelled(
+                        newKeyguardOccludedState: Boolean?
+                    ) {
                         if (newKeyguardOccludedState != null) {
                             keyguardViewMediatorLazy
                                 .get()
@@ -925,7 +927,7 @@
                         // collapse the shade (or at least run the // post collapse
                         // runnables) later on.
                         centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
-                        delegate.onLaunchAnimationCancelled(newKeyguardOccludedState)
+                        delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 4019436..9052409 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -38,7 +38,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
 import com.android.systemui.Dumpable;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.display.data.repository.DisplayMetricsRepository;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -334,6 +334,6 @@
     /**
      * Gets an animation controller from a notification row.
      */
-    ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification(
+    ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification(
             ExpandableNotificationRow associatedView);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 60dfaa7..8af7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -20,7 +20,7 @@
 import android.view.MotionEvent
 import androidx.lifecycle.LifecycleRegistry
 import com.android.keyguard.AuthKeyguardMessageArea
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.navigationbar.NavigationBarView
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.qs.QSPanelController
@@ -99,5 +99,5 @@
     override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
     override fun getAnimatorControllerFromNotification(
         associatedView: ExpandableNotificationRow?,
-    ): ActivityLaunchAnimator.Controller? = null
+    ): ActivityTransitionAnimator.Controller? = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 3e7089c..35aa3df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -115,7 +115,7 @@
 import com.android.systemui.InitController;
 import com.android.systemui.Prefs;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.back.domain.interactor.BackActionInteractor;
 import com.android.systemui.biometrics.AuthRippleController;
@@ -576,7 +576,7 @@
     private boolean mNoAnimationOnNextBarModeChange;
     private final SysuiStatusBarStateController mStatusBarStateController;
 
-    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final ActivityTransitionAnimator mActivityTransitionAnimator;
     private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     private final Lazy<NotificationPresenter> mPresenterLazy;
     private final Lazy<NotificationActivityStarter> mNotificationActivityStarterLazy;
@@ -694,7 +694,7 @@
             @Main MessageRouter messageRouter,
             WallpaperManager wallpaperManager,
             Optional<StartingSurface> startingSurfaceOptional,
-            ActivityLaunchAnimator activityLaunchAnimator,
+            ActivityTransitionAnimator activityTransitionAnimator,
             DeviceStateManager deviceStateManager,
             WiredChargingRippleController wiredChargingRippleController,
             IDreamManager dreamManager,
@@ -816,7 +816,7 @@
         shadeExpansionListener.onPanelExpansionChanged(currentState);
 
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mActivityTransitionAnimator = activityTransitionAnimator;
 
         // TODO(b/190746471): Find a better home for this.
         DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1424,8 +1424,8 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback);
-        mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
+        mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback);
+        mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener);
         mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
         mStackScrollerController.setNotificationActivityStarter(
                 mNotificationActivityStarterLazy.get());
@@ -3176,8 +3176,8 @@
                 }
             };
 
-    private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback =
-            new ActivityLaunchAnimator.Callback() {
+    private final ActivityTransitionAnimator.Callback mActivityTransitionAnimatorCallback =
+            new ActivityTransitionAnimator.Callback() {
                 @Override
                 public boolean isOnKeyguard() {
                     return mKeyguardStateController.isShowing();
@@ -3205,15 +3205,15 @@
                 }
             };
 
-    private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
-            new ActivityLaunchAnimator.Listener() {
+    private final ActivityTransitionAnimator.Listener mActivityTransitionAnimatorListener =
+            new ActivityTransitionAnimator.Listener() {
                 @Override
-                public void onLaunchAnimationStart() {
+                public void onTransitionAnimationStart() {
                     mKeyguardViewMediator.setBlursDisabledForAppLaunch(true);
                 }
 
                 @Override
-                public void onLaunchAnimationEnd() {
+                public void onTransitionAnimationEnd() {
                     mKeyguardViewMediator.setBlursDisabledForAppLaunch(false);
                 }
             };
@@ -3267,7 +3267,7 @@
     }
 
     @Override
-    public ActivityLaunchAnimator.Controller getAnimatorControllerFromNotification(
+    public ActivityTransitionAnimator.Controller getAnimatorControllerFromNotification(
             ExpandableNotificationRow associatedView) {
         return mNotificationAnimationProvider.getAnimatorController(associatedView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index d43f470..7ff5f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,7 +1,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.View
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.TransitionAnimator
 import com.android.systemui.shade.ShadeController
 import com.android.systemui.shade.ShadeViewController
@@ -9,17 +9,17 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController
 
 /**
- * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
+ * A [ActivityTransitionAnimator.Controller] that takes care of collapsing the status bar at the right
  * time.
  */
 class StatusBarLaunchAnimatorController(
-    private val delegate: ActivityLaunchAnimator.Controller,
+    private val delegate: ActivityTransitionAnimator.Controller,
     private val shadeViewController: ShadeViewController,
     private val shadeAnimationInteractor: ShadeAnimationInteractor,
     private val shadeController: ShadeController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val isLaunchForActivity: Boolean = true
-) : ActivityLaunchAnimator.Controller by delegate {
+) : ActivityTransitionAnimator.Controller by delegate {
     // Always sync the opening window with the shade, given that we draw a hole punch in the shade
     // of the same size and position as the opening app to make it visible.
     override val openingWindowSyncView: View?
@@ -39,7 +39,7 @@
         shadeAnimationInteractor.setIsLaunchingActivity(true)
         if (!isExpandingFullyAbove) {
             shadeViewController.collapseWithDuration(
-                ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
+                ActivityTransitionAnimator.TIMINGS.totalDuration.toInt())
         }
     }
 
@@ -58,8 +58,8 @@
         shadeViewController.applyLaunchAnimationProgress(linearProgress)
     }
 
-    override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
-        delegate.onLaunchAnimationCancelled()
+    override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+        delegate.onTransitionAnimationCancelled()
         shadeAnimationInteractor.setIsLaunchingActivity(false)
         shadeController.onLaunchAnimationCancelled(isLaunchForActivity)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4ee061d..2737580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,7 +52,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.EventLogTags;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -125,7 +125,7 @@
     private final NotificationPresenter mPresenter;
     private final ShadeViewController mShadeViewController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
-    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final ActivityTransitionAnimator mActivityTransitionAnimator;
     private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     private final PowerInteractor mPowerInteractor;
     private final UserTracker mUserTracker;
@@ -161,7 +161,7 @@
             NotificationPresenter presenter,
             ShadeViewController shadeViewController,
             NotificationShadeWindowController notificationShadeWindowController,
-            ActivityLaunchAnimator activityLaunchAnimator,
+            ActivityTransitionAnimator activityTransitionAnimator,
             ShadeAnimationInteractor shadeAnimationInteractor,
             NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
             LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
@@ -194,7 +194,7 @@
         mOnUserInteractionCallback = onUserInteractionCallback;
         mPresenter = presenter;
         mShadeViewController = shadeViewController;
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mActivityTransitionAnimator = activityTransitionAnimator;
         mNotificationAnimationProvider = notificationAnimationProvider;
         mPowerInteractor = powerInteractor;
         mUserTracker = userTracker;
@@ -440,7 +440,7 @@
             boolean isActivityIntent) {
         mLogger.logStartNotificationIntent(entry);
         try {
-            ActivityLaunchAnimator.Controller animationController =
+            ActivityTransitionAnimator.Controller animationController =
                     new StatusBarLaunchAnimatorController(
                             mNotificationAnimationProvider.getAnimatorController(row, null),
                             mShadeViewController,
@@ -448,7 +448,7 @@
                             mShadeController,
                             mNotificationShadeWindowController,
                             isActivityIntent);
-            mActivityLaunchAnimator.startPendingIntentWithAnimation(
+            mActivityTransitionAnimator.startPendingIntentWithAnimation(
                     animationController,
                     animate,
                     intent.getCreatorPackage(),
@@ -482,7 +482,7 @@
             @Override
             public boolean onDismiss() {
                 AsyncTask.execute(() -> {
-                    ActivityLaunchAnimator.Controller animationController =
+                    ActivityTransitionAnimator.Controller animationController =
                             new StatusBarLaunchAnimatorController(
                                     mNotificationAnimationProvider.getAnimatorController(row),
                                     mShadeViewController,
@@ -491,7 +491,7 @@
                                     mNotificationShadeWindowController,
                                     true /* isActivityIntent */);
 
-                    mActivityLaunchAnimator.startIntentWithAnimation(
+                    mActivityTransitionAnimator.startIntentWithAnimation(
                             animationController, animate, intent.getPackage(),
                             (adapter) -> TaskStackBuilder.create(mContext)
                                     .addNextIntentWithParentStack(intent)
@@ -528,11 +528,11 @@
                         tsb.addNextIntent(intent);
                     }
 
-                    ActivityLaunchAnimator.Controller viewController =
-                            ActivityLaunchAnimator.Controller.fromView(view,
+                    ActivityTransitionAnimator.Controller viewController =
+                            ActivityTransitionAnimator.Controller.fromView(view,
                                     InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
                             );
-                    ActivityLaunchAnimator.Controller animationController =
+                    ActivityTransitionAnimator.Controller animationController =
                             viewController == null ? null
                                 : new StatusBarLaunchAnimatorController(
                                         viewController,
@@ -542,8 +542,8 @@
                                         mNotificationShadeWindowController,
                                         true /* isActivityIntent */);
 
-                    mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
-                            intent.getPackage(),
+                    mActivityTransitionAnimator.startIntentWithAnimation(
+                            animationController, animate, intent.getPackage(),
                             (adapter) -> tsb.startActivities(
                                     getActivityOptions(mDisplayId, adapter),
                                     mUserTracker.getUserHandle()));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 0bdd1a5..a20468f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -30,7 +30,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.Dumpable
 import com.android.systemui.res.R
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
@@ -233,7 +233,7 @@
                 logger.logChipClicked()
                 activityStarter.postStartActivityDismissingKeyguard(
                     intent,
-                    ActivityLaunchAnimator.Controller.fromView(
+                    ActivityTransitionAnimator.Controller.fromView(
                         backgroundView,
                         InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index 21d3fa4..65c2e20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -47,7 +47,7 @@
 import android.view.WindowManager;
 
 import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DelegateLaunchAnimatorController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -188,8 +188,8 @@
      *   updated animation controller that handles status-bar-related animation details. Returns an
      *   empty optional if the animation is *not* on a view in the status bar.
      */
-    public Optional<ActivityLaunchAnimator.Controller> wrapAnimationControllerIfInStatusBar(
-            View rootView, ActivityLaunchAnimator.Controller animationController) {
+    public Optional<ActivityTransitionAnimator.Controller> wrapAnimationControllerIfInStatusBar(
+            View rootView, ActivityTransitionAnimator.Controller animationController) {
         if (rootView != mStatusBarWindowView) {
             return Optional.empty();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index 3376e23..147e158 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.theme;
 
+import static com.android.systemui.shared.Flags.enableHomeDelay;
+
 import android.annotation.AnyThread;
 import android.content.om.FabricatedOverlay;
 import android.content.om.OverlayIdentifier;
@@ -32,6 +34,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 
 import com.google.android.collect.Lists;
@@ -142,6 +145,7 @@
     private final Map<String, String> mCategoryToTargetPackage = new ArrayMap<>();
     private final OverlayManager mOverlayManager;
     private final Executor mBgExecutor;
+    private final Executor mMainExecutor;
     private final String mLauncherPackage;
     private final String mThemePickerPackage;
 
@@ -150,9 +154,11 @@
             @Background Executor bgExecutor,
             @Named(ThemeModule.LAUNCHER_PACKAGE) String launcherPackage,
             @Named(ThemeModule.THEME_PICKER_PACKAGE) String themePickerPackage,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            @Main Executor mainExecutor) {
         mOverlayManager = overlayManager;
         mBgExecutor = bgExecutor;
+        mMainExecutor = mainExecutor;
         mLauncherPackage = launcherPackage;
         mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
@@ -184,12 +190,21 @@
     /**
      * Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that
      * affect sysui will also be applied to the system user.
+     *
+     * @param categoryToPackage Overlay packages to be applied
+     * @param pendingCreation Overlays yet to be created
+     * @param currentUser Current User ID
+     * @param managedProfiles Profiles get overlays
+     * @param onComplete Callback for when resources are ready. Runs in the main thread.
      */
     public void applyCurrentUserOverlays(
             Map<String, OverlayIdentifier> categoryToPackage,
             FabricatedOverlay[] pendingCreation,
             int currentUser,
-            Set<UserHandle> managedProfiles) {
+            Set<UserHandle> managedProfiles,
+            Runnable onComplete
+    ) {
+
         mBgExecutor.execute(() -> {
 
             // Disable all overlays that have not been specified in the user setting.
@@ -236,6 +251,10 @@
 
             try {
                 mOverlayManager.commit(transaction.build());
+                if (enableHomeDelay() && onComplete != null) {
+                    Log.d(TAG, "Executing onComplete runnable");
+                    mMainExecutor.execute(onComplete);
+                }
             } catch (SecurityException | IllegalStateException e) {
                 Log.e(TAG, "setEnabled failed", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 2b9ad50..585ab72 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -20,6 +20,7 @@
 
 import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.shared.Flags.enableHomeDelay;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
@@ -31,6 +32,7 @@
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE;
 import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD;
 
+import android.app.ActivityManager;
 import android.app.UiModeManager;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
@@ -140,6 +142,7 @@
     // Current wallpaper colors associated to a user.
     private final SparseArray<WallpaperColors> mCurrentColors = new SparseArray<>();
     private final WallpaperManager mWallpaperManager;
+    private final ActivityManager mActivityManager;
     @VisibleForTesting
     protected ColorScheme mColorScheme;
     // If fabricated overlays were already created for the current theme.
@@ -414,7 +417,8 @@
             WakefulnessLifecycle wakefulnessLifecycle,
             JavaAdapter javaAdapter,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
-            UiModeManager uiModeManager) {
+            UiModeManager uiModeManager,
+            ActivityManager activityManager) {
         mContext = context;
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
         mIsFidelityEnabled = featureFlags.isEnabled(Flags.COLOR_FIDELITY);
@@ -433,6 +437,7 @@
         mJavaAdapter = javaAdapter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mUiModeManager = uiModeManager;
+        mActivityManager = activityManager;
         dumpManager.registerDumpable(TAG, this);
     }
 
@@ -806,8 +811,16 @@
             }
         }
 
+        final Runnable onCompleteCallback = !enableHomeDelay()
+                ? () -> {}
+                : () -> {
+                    Log.d(TAG, "ThemeHomeDelay: ThemeOverlayController ready");
+                    mActivityManager.setThemeOverlayReady(true);
+                };
+
         if (colorSchemeIsApplied(managedProfiles)) {
             Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme);
+            onCompleteCallback.run();
             return;
         }
 
@@ -816,15 +829,19 @@
                     .map(key -> key + " -> " + categoryToPackage.get(key)).collect(
                             Collectors.joining(", ")));
         }
+
+        FabricatedOverlay[] fOverlays = null;
+
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
-            mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[]{
+            fOverlays = new FabricatedOverlay[]{
                     mSecondaryOverlay, mNeutralOverlay, mDynamicOverlay
-            }, currentUser, managedProfiles);
-        } else {
-            mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
-                    managedProfiles);
+            };
         }
+
+        mThemeManager.applyCurrentUserOverlays(categoryToPackage, fOverlays, currentUser,
+                managedProfiles, onCompleteCallback);
+
     }
 
     private Style fetchThemeStyleFromSetting() {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 0fb4b43..38b381a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.user.domain.interactor
 
 import android.annotation.UserIdInt
+import android.content.pm.UserInfo
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Flags.refactorGetCurrentUser
 import com.android.systemui.dagger.SysUISingleton
@@ -16,6 +17,9 @@
     /** Flow providing the ID of the currently selected user. */
     val selectedUser = repository.selectedUserInfo.map { it.id }.distinctUntilChanged()
 
+    /** Flow providing the [UserInfo] of the currently selected user. */
+    val selectedUserInfo = repository.selectedUserInfo
+
     /**
      * Returns the ID of the currently-selected user.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 278ffc6..1af5c46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -18,6 +18,9 @@
 
 import android.content.Context
 import android.media.AudioManager
+import com.android.settingslib.media.data.repository.SpatializerRepository
+import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
+import com.android.settingslib.media.domain.interactor.SpatializerInteractor
 import com.android.settingslib.volume.data.repository.AudioRepository
 import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
@@ -54,5 +57,16 @@
         @Provides
         fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor =
             AudioModeInteractor(repository)
+
+        @Provides
+        fun provdieSpatializerRepository(
+            audioManager: AudioManager,
+            @Background backgroundContext: CoroutineContext,
+        ): SpatializerRepository =
+            SpatializerRepositoryImpl(audioManager.spatializer, backgroundContext)
+
+        @Provides
+        fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
+            SpatializerInteractor(repository)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index e0228d9..1d9b90a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -34,7 +34,7 @@
 import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
 import android.util.Log;
 
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -236,12 +236,12 @@
      * that too is null, then fall back to {@link WalletActivity}.
      *
      * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent.
-     * @param animationController an {@link ActivityLaunchAnimator.Controller} to provide a
+     * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a
      *                            smooth animation for the activity launch.
      * @param hasCard whether the service returns any cards.
      */
     public void startQuickAccessUiIntent(ActivityStarter activityStarter,
-            ActivityLaunchAnimator.Controller animationController,
+            ActivityTransitionAnimator.Controller animationController,
             boolean hasCard) {
         mQuickAccessWalletClient.getWalletPendingIntent(mExecutor,
                 walletPendingIntent -> {
@@ -271,7 +271,7 @@
     private void startQuickAccessViaIntent(Intent intent,
             boolean hasCard,
             ActivityStarter activityStarter,
-            ActivityLaunchAnimator.Controller animationController) {
+            ActivityTransitionAnimator.Controller animationController) {
         if (hasCard) {
             activityStarter.startActivity(intent, true /* dismissShade */,
                     animationController, true /* showOverLockscreenWhenLocked */);
@@ -285,7 +285,7 @@
 
     private void startQuickAccessViaPendingIntent(PendingIntent pendingIntent,
             ActivityStarter activityStarter,
-            ActivityLaunchAnimator.Controller animationController) {
+            ActivityTransitionAnimator.Controller animationController) {
         activityStarter.postStartActivityDismissingKeyguard(
                 pendingIntent,
                 animationController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
similarity index 79%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 722107c..75a49d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -44,37 +44,37 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
-class ActivityLaunchAnimatorTest : SysuiTestCase() {
+class ActivityTransitionAnimatorTest : SysuiTestCase() {
     private val transitionContainer = LinearLayout(mContext)
     private val testTransitionAnimator = fakeTransitionAnimator()
-    @Mock lateinit var callback: ActivityLaunchAnimator.Callback
-    @Mock lateinit var listener: ActivityLaunchAnimator.Listener
-    @Spy private val controller = TestLaunchAnimatorController(transitionContainer)
+    @Mock lateinit var callback: ActivityTransitionAnimator.Callback
+    @Mock lateinit var listener: ActivityTransitionAnimator.Listener
+    @Spy private val controller = TestTransitionAnimatorController(transitionContainer)
     @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
 
-    private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+    private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
     @get:Rule val rule = MockitoJUnit.rule()
 
     @Before
     fun setup() {
-        activityLaunchAnimator =
-            ActivityLaunchAnimator(
+        activityTransitionAnimator =
+            ActivityTransitionAnimator(
                 testTransitionAnimator,
                 testTransitionAnimator,
                 disableWmTimeout = true
             )
-        activityLaunchAnimator.callback = callback
-        activityLaunchAnimator.addListener(listener)
+        activityTransitionAnimator.callback = callback
+        activityTransitionAnimator.addListener(listener)
     }
 
     @After
     fun tearDown() {
-        activityLaunchAnimator.removeListener(listener)
+        activityTransitionAnimator.removeListener(listener)
     }
 
     private fun startIntentWithAnimation(
-        animator: ActivityLaunchAnimator = this.activityLaunchAnimator,
-        controller: ActivityLaunchAnimator.Controller? = this.controller,
+        animator: ActivityTransitionAnimator = this.activityTransitionAnimator,
+        controller: ActivityTransitionAnimator.Controller? = this.controller,
         animate: Boolean = true,
         intentStarter: (RemoteAnimationAdapter?) -> Int
     ) {
@@ -138,7 +138,7 @@
         val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
         var animationAdapter: RemoteAnimationAdapter? = null
 
-        startIntentWithAnimation(activityLaunchAnimator) { adapter ->
+        startIntentWithAnimation(activityTransitionAnimator) { adapter ->
             animationAdapter = adapter
             ActivityManager.START_DELIVERED_TO_TOP
         }
@@ -163,50 +163,50 @@
 
     @Test
     fun doesNotStartIfAnimationIsCancelled() {
-        val runner = activityLaunchAnimator.createRunner(controller)
+        val runner = activityTransitionAnimator.createRunner(controller)
         runner.onAnimationCancelled()
         runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
 
         waitForIdleSync()
-        verify(controller).onLaunchAnimationCancelled()
+        verify(controller).onTransitionAnimationCancelled()
         verify(controller, never()).onTransitionAnimationStart(anyBoolean())
-        verify(listener).onLaunchAnimationCancelled()
-        verify(listener, never()).onLaunchAnimationStart()
+        verify(listener).onTransitionAnimationCancelled()
+        verify(listener, never()).onTransitionAnimationStart()
         assertNull(runner.delegate)
     }
 
     @Test
     fun cancelsIfNoOpeningWindowIsFound() {
-        val runner = activityLaunchAnimator.createRunner(controller)
+        val runner = activityTransitionAnimator.createRunner(controller)
         runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
 
         waitForIdleSync()
-        verify(controller).onLaunchAnimationCancelled()
+        verify(controller).onTransitionAnimationCancelled()
         verify(controller, never()).onTransitionAnimationStart(anyBoolean())
-        verify(listener).onLaunchAnimationCancelled()
-        verify(listener, never()).onLaunchAnimationStart()
+        verify(listener).onTransitionAnimationCancelled()
+        verify(listener, never()).onTransitionAnimationStart()
         assertNull(runner.delegate)
     }
 
     @Test
     fun startsAnimationIfWindowIsOpening() {
-        val runner = activityLaunchAnimator.createRunner(controller)
+        val runner = activityTransitionAnimator.createRunner(controller)
         runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
         waitForIdleSync()
-        verify(listener).onLaunchAnimationStart()
+        verify(listener).onTransitionAnimationStart()
         verify(controller).onTransitionAnimationStart(anyBoolean())
     }
 
     @Test
     fun creatingControllerFromNormalViewThrows() {
         assertThrows(IllegalArgumentException::class.java) {
-            ActivityLaunchAnimator.Controller.fromView(FrameLayout(mContext))
+            ActivityTransitionAnimator.Controller.fromView(FrameLayout(mContext))
         }
     }
 
     @Test
     fun disposeRunner_delegateDereferenced() {
-        val runner = activityLaunchAnimator.createRunner(controller)
+        val runner = activityTransitionAnimator.createRunner(controller)
         assertNotNull(runner.delegate)
         runner.dispose()
         waitForIdleSync()
@@ -241,11 +241,11 @@
 }
 
 /**
- * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
+ * A simple implementation of [ActivityTransitionAnimator.Controller] which throws if it is called
  * outside of the main thread.
  */
-private class TestLaunchAnimatorController(override var transitionContainer: ViewGroup) :
-    ActivityLaunchAnimator.Controller {
+private class TestTransitionAnimatorController(override var transitionContainer: ViewGroup) :
+    ActivityTransitionAnimator.Controller {
     override fun createAnimatorState() =
         TransitionAnimator.State(
             top = 100,
@@ -282,7 +282,7 @@
         assertOnMainThread()
     }
 
-    override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+    override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
         assertOnMainThread()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 24cf164..2732047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -86,7 +86,7 @@
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollectorFake;
@@ -186,7 +186,7 @@
     private @Mock ShadeController mShadeController;
     private NotificationShadeWindowController mNotificationShadeWindowController;
     private @Mock DreamOverlayStateController mDreamOverlayStateController;
-    private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+    private @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
     private @Mock ScrimController mScrimController;
     private @Mock IActivityTaskManager mActivityTaskManagerService;
     private @Mock SysuiColorExtractor mColorExtractor;
@@ -763,7 +763,7 @@
 
     @Test
     public void testUpdateIsKeyguardAfterOccludeAnimationIsCancelled() {
-        mViewMediator.mOccludeAnimationController.onLaunchAnimationCancelled(
+        mViewMediator.mOccludeAnimationController.onTransitionAnimationCancelled(
                 null /* newKeyguardOccludedState */);
 
         // Since the updateIsKeyguard call is delayed during the animation, ensure it's called if
@@ -1231,7 +1231,7 @@
                 mWallpaperRepository,
                 () -> mShadeController,
                 () -> mNotificationShadeWindowController,
-                () -> mActivityLaunchAnimator,
+                () -> mActivityTransitionAnimator,
                 () -> mScrimController,
                 mActivityTaskManagerService,
                 mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index b4ae7e3..798c7f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -24,7 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
@@ -225,7 +225,7 @@
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var activityStarter: ActivityStarter
-    @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
+    @Mock private lateinit var animationController: ActivityTransitionAnimator.Controller
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS
deleted file mode 100644
index 142862d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Haptics team also works on Ringtones (RingtonePlayer/NotificationPlayer)
-file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index c6cfabc..32b6f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -72,7 +72,7 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -110,7 +110,7 @@
     @Mock
     private DialogLaunchAnimator mDialogLaunchAnimator;
     @Mock
-    private ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController;
+    private ActivityTransitionAnimator.Controller mActivityTransitionAnimatorController;
     @Mock
     private NearbyMediaDevicesManager mNearbyMediaDevicesManager;
     // Mock
@@ -143,7 +143,7 @@
     @Mock
     private KeyguardManager mKeyguardManager;
     @Mock
-    private ActivityLaunchAnimator.Controller mController;
+    private ActivityTransitionAnimator.Controller mController;
     @Mock
     private PowerExemptionManager mPowerExemptionManager;
     @Mock
@@ -1122,7 +1122,7 @@
     @Test
     public void launchBluetoothPairing_isKeyguardLocked_dismissDialog() {
         when(mDialogLaunchAnimator.createActivityLaunchController(mDialogLaunchView)).thenReturn(
-                mActivityLaunchAnimatorController);
+                mActivityTransitionAnimatorController);
         when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
         mMediaOutputController.mCallback = this.mCallback;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index ae47a7b..33f8f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -38,7 +38,7 @@
 import android.view.View
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.view.LaunchableFrameLayout
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
@@ -372,7 +372,7 @@
 
         verify(activityStarter)
             .startPendingIntentMaybeDismissingKeyguard(
-                eq(pi), nullable(), nullable<ActivityLaunchAnimator.Controller>())
+                eq(pi), nullable(), nullable<ActivityTransitionAnimator.Controller>())
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 23466cc..720c25a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -26,7 +26,7 @@
 import com.android.internal.logging.testing.FakeMetricsLogger
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.globalactions.GlobalActionsDialogLite
 import com.android.systemui.plugins.ActivityStarter
@@ -127,7 +127,7 @@
             .startActivity(
                 intentCaptor.capture(),
                 /* dismissShade= */ eq(true),
-                nullable() as? ActivityLaunchAnimator.Controller,
+                nullable() as? ActivityTransitionAnimator.Controller,
             )
         assertThat(intentCaptor.value.action).isEqualTo(Settings.ACTION_SETTINGS)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 3bf59ca..874368b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -29,7 +29,7 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlInfo
@@ -348,7 +348,7 @@
         verify(activityStarter).startActivity(
                 intentCaptor.capture(),
                 eq(true) /* dismissShade */,
-                nullable(ActivityLaunchAnimator.Controller::class.java),
+                nullable(ActivityTransitionAnimator.Controller::class.java),
                 eq(true) /* showOverLockscreenWhenLocked */)
         assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
     }
@@ -379,7 +379,7 @@
         verify(activityStarter).startActivity(
                 intentCaptor.capture(),
                 anyBoolean() /* dismissShade */,
-                nullable(ActivityLaunchAnimator.Controller::class.java),
+                nullable(ActivityTransitionAnimator.Controller::class.java),
                 eq(false) /* showOverLockscreenWhenLocked */)
         assertThat(intentCaptor.value.component?.className).isEqualTo(CONTROLS_ACTIVITY_CLASS_NAME)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 9517f82..1dc5f7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -103,8 +103,6 @@
             )
         testableLooper = TestableLooper.get(this)
 
-        communalRepository.setIsCommunalEnabled(true)
-
         whenever(keyguardTransitionInteractor.isFinishedInStateWhere(any()))
             .thenReturn(bouncerShowingFlow)
         whenever(shadeInteractor.isAnyFullyExpanded).thenReturn(shadeShowingFlow)
@@ -125,36 +123,6 @@
     }
 
     @Test
-    fun isEnabled_communalEnabled_returnsTrue() {
-        communalRepository.setIsCommunalEnabled(true)
-
-        assertThat(underTest.isEnabled()).isTrue()
-    }
-
-    @Test
-    fun isEnabled_communalDisabled_returnsFalse() {
-        communalRepository.setIsCommunalEnabled(false)
-
-        assertThat(underTest.isEnabled()).isFalse()
-    }
-
-    @Test
-    fun initView_notEnabled_throwsException() {
-        communalRepository.setIsCommunalEnabled(false)
-
-        underTest =
-            GlanceableHubContainerController(
-                communalInteractor,
-                communalViewModel,
-                keyguardTransitionInteractor,
-                shadeInteractor,
-                powerManager,
-            )
-
-        assertThrows(RuntimeException::class.java) { underTest.initView(context) }
-    }
-
-    @Test
     fun initView_calledTwice_throwsException() {
         underTest =
             GlanceableHubContainerController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 22b05be..248ed24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -488,7 +488,8 @@
             return
         }
 
-        whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(true)
+        whenever(mGlanceableHubContainerController.communalAvailable())
+            .thenReturn(MutableStateFlow(true))
 
         val mockCommunalView = mock(View::class.java)
         whenever(mGlanceableHubContainerController.initView(any<Context>()))
@@ -513,7 +514,6 @@
             return
         }
 
-        whenever(mGlanceableHubContainerController.isEnabled()).thenReturn(false)
         whenever(mGlanceableHubContainerController.communalAvailable())
             .thenReturn(MutableStateFlow(false))
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index f58ff0a..3315e68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -97,7 +97,7 @@
     @Test
     fun testHunIsRemovedAndCallbackIsInvokedWhenAnimationIsCancelled() {
         flagNotificationAsHun()
-        controller.onLaunchAnimationCancelled()
+        controller.onTransitionAnimationCancelled()
 
         assertTrue(HeadsUpUtil.isClickedHeadsUpNotification(notification))
         assertFalse(notification.entry.isExpandAnimationRunning)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index b922ab3..3811f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -472,6 +472,23 @@
     }
 
     @Test
+    public void publicMode_nullChannel_allowed() {
+        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState(mEntry);
+
+        // WHEN the notification's user is in public mode and settings are configured to disallow
+        // notifications in public mode
+        when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
+        mEntry.setRanking(new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setVisibilityOverride(VISIBILITY_SECRET).build());
+
+        // THEN allow the entry
+        assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
+    }
+
+    @Test
     public void publicMode_notifDisallowed() {
         mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
         NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 5a57035..dfbe1ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -18,8 +18,10 @@
 
 import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
@@ -28,6 +30,7 @@
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,9 +41,12 @@
 import org.mockito.Mockito.verify
 
 private const val VIEW_HEIGHT = 100
+private const val FULL_SHADE_APPEAR_TRANSLATION = 300
+private const val HEADS_UP_ABOVE_SCREEN = 80
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@RunWithLooper
 class StackStateAnimatorTest : SysuiTestCase() {
 
     private lateinit var stackStateAnimator: StackStateAnimator
@@ -51,9 +57,15 @@
     private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
     @Before
     fun setUp() {
+        overrideResource(
+            R.dimen.go_to_full_shade_appearing_translation,
+            FULL_SHADE_APPEAR_TRANSLATION
+        )
+        overrideResource(R.dimen.heads_up_appear_y_above_screen, HEADS_UP_ABOVE_SCREEN)
+
         whenever(stackScroller.context).thenReturn(context)
         whenever(view.viewState).thenReturn(viewState)
-        stackStateAnimator = StackStateAnimator(stackScroller)
+        stackStateAnimator = StackStateAnimator(mContext, stackScroller)
     }
 
     @Test
@@ -122,4 +134,22 @@
         verify(view, description("should be called at the end of the animation"))
             .removeFromTransientContainer()
     }
+
+    @Test
+    fun initView_updatesResources() {
+        // Given: the resource values are initialized in the SSA
+        assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation)
+            .isEqualTo(FULL_SHADE_APPEAR_TRANSLATION)
+        assertThat(stackStateAnimator.mHeadsUpAppearStartAboveScreen)
+            .isEqualTo(HEADS_UP_ABOVE_SCREEN)
+
+        // When: initView is called after the resources have changed
+        overrideResource(R.dimen.go_to_full_shade_appearing_translation, 200)
+        overrideResource(R.dimen.heads_up_appear_y_above_screen, 100)
+        stackStateAnimator.initView(mContext)
+
+        // Then: the resource values are updated
+        assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation).isEqualTo(200)
+        assertThat(stackStateAnimator.mHeadsUpAppearStartAboveScreen).isEqualTo(100)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index b048949..9c60a2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -83,7 +83,7 @@
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.back.domain.interactor.BackActionInteractor;
 import com.android.systemui.biometrics.AuthRippleController;
@@ -307,7 +307,7 @@
     @Mock private StartingSurface mStartingSurface;
     @Mock private OperatorNameViewController mOperatorNameViewController;
     @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
-    @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
+    @Mock private ActivityTransitionAnimator mActivityTransitionAnimator;
     @Mock private DeviceStateManager mDeviceStateManager;
     @Mock private WiredChargingRippleController mWiredChargingRippleController;
     @Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@@ -543,7 +543,7 @@
                 new MessageRouterImpl(mMainExecutor),
                 mWallpaperManager,
                 Optional.of(mStartingSurface),
-                mActivityLaunchAnimator,
+                mActivityTransitionAnimator,
                 mDeviceStateManager,
                 mWiredChargingRippleController,
                 mDreamManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 597e2e3..41514ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -63,7 +63,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.flags.FakeFeatureFlags;
@@ -159,7 +159,7 @@
     @Mock
     private StatusBarNotificationActivityStarter mNotificationActivityStarter;
     @Mock
-    private ActivityLaunchAnimator mActivityLaunchAnimator;
+    private ActivityTransitionAnimator mActivityTransitionAnimator;
     @Mock
     private InteractionJankMonitor mJankMonitor;
     private FakePowerRepository mPowerRepository;
@@ -255,7 +255,7 @@
                         mock(NotificationPresenter.class),
                         mock(ShadeViewController.class),
                         mock(NotificationShadeWindowController.class),
-                        mActivityLaunchAnimator,
+                        mActivityTransitionAnimator,
                         new ShadeAnimationInteractorLegacyImpl(
                                 new ShadeAnimationRepository(), new FakeShadeRepository()),
                         notificationAnimationProvider,
@@ -306,7 +306,7 @@
         // Then
         verify(mShadeController, atLeastOnce()).collapseShade();
 
-        verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
+        verify(mActivityTransitionAnimator).startPendingIntentWithAnimation(any(),
                 eq(false) /* animate */, any(), any());
 
         verify(mAssistManager).hideAssist();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 83439f0..8f4cbaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -104,9 +104,9 @@
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mManager = new ThemeOverlayApplier(mOverlayManager,
-                MoreExecutors.directExecutor(),
-                LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) {
+        mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(),
+                LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager,
+                MoreExecutors.directExecutor()) {
             @Override
             protected OverlayManagerTransaction.Builder getTransactionBuilder() {
                 return mTransactionBuilder;
@@ -179,7 +179,7 @@
     @Test
     public void allCategoriesSpecified_allEnabledExclusively() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, null);
         verify(mOverlayManager).commit(any());
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -191,7 +191,7 @@
     @Test
     public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, null);
 
         for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
             if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -208,7 +208,7 @@
     public void allCategoriesSpecified_enabledForAllUserHandles() {
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, null);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -225,7 +225,7 @@
 
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, null);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
@@ -239,7 +239,7 @@
                 mock(FabricatedOverlay.class)
         };
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
-                TEST_USER.getIdentifier(), TEST_USER_HANDLES);
+                TEST_USER.getIdentifier(), TEST_USER_HANDLES, null);
 
         for (FabricatedOverlay overlay : pendingCreation) {
             verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -253,7 +253,7 @@
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, null);
 
         for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -270,7 +270,7 @@
     @Test
     public void zeroCategoriesSpecified_allDisabled() {
         mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, null);
 
         for (String category : THEME_CATEGORIES) {
             verify(mTransactionBuilder).setEnabled(
@@ -285,7 +285,7 @@
         categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, null);
 
         verify(mTransactionBuilder, never()).setEnabled(
                 eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index b58a41c..c02583a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.UiModeManager;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
@@ -129,6 +130,8 @@
     private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock
     private UiModeManager mUiModeManager;
+    @Mock
+    private ActivityManager mActivityManager;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver;
     @Captor
@@ -164,7 +167,7 @@
                 mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
@@ -224,7 +227,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -249,7 +252,7 @@
         mBroadcastReceiver.getValue().onReceive(null, intent);
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -263,7 +266,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Should not change theme after changing wallpapers, if intent doesn't have
         // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
@@ -272,7 +275,7 @@
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -294,7 +297,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -333,7 +336,7 @@
                 .isFalse();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -367,8 +370,7 @@
         assertThat(updatedSetting.getValue().contains(
                 "android.theme.customization.color_both\":\"0")).isTrue();
 
-        verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -423,7 +425,7 @@
         assertThat(updatedSetting.getValue().contains(
                 "android.theme.customization.color_both\":\"1")).isTrue();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -492,7 +494,7 @@
                 "android.theme.customization.color_both\":\"1")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -523,7 +525,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -554,7 +556,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -587,7 +589,7 @@
                 anyInt());
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -620,7 +622,7 @@
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
 
         // Apply overlay by existing theme from secure setting
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -653,7 +655,7 @@
 
 
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -675,7 +677,7 @@
         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
                 ArgumentCaptor.forClass(Map.class);
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received secondary user colors
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -689,7 +691,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_PROFILE_ADDED)
                         .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -700,7 +702,7 @@
                 new Intent(Intent.ACTION_PROFILE_ADDED)
                         .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -711,7 +713,7 @@
                 new Intent(Intent.ACTION_PROFILE_ADDED)
                         .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE));
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -723,7 +725,7 @@
                 (new Intent(Intent.ACTION_PROFILE_ADDED))
                         .putExtra(Intent.EXTRA_USER, PRIVATE_USER_HANDLE));
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
 
@@ -737,7 +739,7 @@
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
                 USER_SYSTEM);
 
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Regression test: null events should not reset the internal state and allow colors to be
         // applied again.
@@ -747,11 +749,11 @@
         mBroadcastReceiver.getValue().onReceive(null, intent);
         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
     }
 
     @Test
@@ -770,7 +772,7 @@
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
@@ -791,7 +793,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
     }
 
@@ -810,7 +812,7 @@
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
-                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager) {
+                mJavaAdapter, mKeyguardTransitionInteractor, mUiModeManager, mActivityManager) {
             @VisibleForTesting
             protected boolean isNightMode() {
                 return false;
@@ -831,7 +833,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
 
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -853,12 +855,12 @@
 
         // Defers event because we already have initial colors.
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Then event happens after setup phase is over.
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
         mDeviceProvisionedListener.getValue().onUserSetupChanged();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -881,11 +883,11 @@
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
                 USER_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -907,10 +909,10 @@
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
                 USER_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -930,7 +932,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -949,19 +951,19 @@
         mColorsListener.getValue().onColorsChanged(startingColors, WallpaperManager.FLAG_SYSTEM,
                 USER_SYSTEM);
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
 
         // Set to the same colors.
         mColorsListener.getValue().onColorsChanged(sameColors, WallpaperManager.FLAG_SYSTEM,
                 USER_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Verify that no change resulted.
         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
     }
 
     @Test
@@ -975,7 +977,7 @@
                 ArgumentCaptor.forClass(FabricatedOverlay[].class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), themeOverlays.capture(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), themeOverlays.capture(), anyInt(), any(), any());
 
         FabricatedOverlay[] overlays = themeOverlays.getValue();
         FabricatedOverlay accents = overlays[0];
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 8263174..fccb936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -37,10 +37,10 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -68,7 +68,7 @@
     @Mock
     private ActivityStarter mActivityStarter;
     @Mock
-    private ActivityLaunchAnimator.Controller mAnimationController;
+    private ActivityTransitionAnimator.Controller mAnimationController;
     @Captor
     private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
     @Captor
@@ -219,7 +219,7 @@
     public void getQuickAccessUiIntent_hasCards_noPendingIntent_startsWalletActivity() {
         mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true);
         verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true),
-                any(ActivityLaunchAnimator.Controller.class), eq(true));
+                any(ActivityTransitionAnimator.Controller.class), eq(true));
         Intent intent = mIntentCaptor.getValue();
         assertEquals(intent.getAction(), Intent.ACTION_VIEW);
         assertEquals(
@@ -231,7 +231,7 @@
     public void getQuickAccessUiIntent_noCards_noPendingIntent_startsWalletActivity() {
         mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, false);
         verify(mActivityStarter).postStartActivityDismissingKeyguard(mIntentCaptor.capture(), eq(0),
-                any(ActivityLaunchAnimator.Controller.class));
+                any(ActivityTransitionAnimator.Controller.class));
         Intent intent = mIntentCaptor.getValue();
         assertEquals(intent.getAction(), Intent.ACTION_VIEW);
         assertEquals(
@@ -254,7 +254,7 @@
         }).when(mQuickAccessWalletClient).getWalletPendingIntent(any(), any());
         mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, true);
         verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntentCaptor.capture(),
-                any(ActivityLaunchAnimator.Controller.class));
+                any(ActivityTransitionAnimator.Controller.class));
         PendingIntent pendingIntent = mPendingIntentCaptor.getValue();
         Intent intent = pendingIntent.getIntent();
         assertEquals(intent.getAction(), Intent.ACTION_VIEW);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d45a9a9..8d933dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -176,9 +176,11 @@
 import com.android.wm.shell.bubbles.BubbleDataRepository;
 import com.android.wm.shell.bubbles.BubbleEducationController;
 import com.android.wm.shell.bubbles.BubbleEntry;
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
 import com.android.wm.shell.bubbles.BubbleLogger;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubbleStackView;
+import com.android.wm.shell.bubbles.BubbleTaskView;
 import com.android.wm.shell.bubbles.BubbleViewInfoTask;
 import com.android.wm.shell.bubbles.BubbleViewProvider;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -195,6 +197,7 @@
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.taskview.TaskView;
 import com.android.wm.shell.taskview.TaskViewTransitions;
 import com.android.wm.shell.transition.Transitions;
 
@@ -214,6 +217,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Optional;
+import java.util.concurrent.Executor;
 
 import kotlinx.coroutines.test.TestScope;
 
@@ -1416,7 +1420,9 @@
                 .thenReturn(userContext);
 
         BubbleViewInfoTask.BubbleViewInfo info = BubbleViewInfoTask.BubbleViewInfo.populate(context,
-                mBubbleController,
+                BubbleExpandedViewManager.fromBubbleController(mBubbleController),
+                () -> new BubbleTaskView(mock(TaskView.class), mock(Executor.class)),
+                mPositioner,
                 mBubbleController.getStackView(),
                 new BubbleIconFactory(mContext,
                         mContext.getResources().getDimensionPixelSize(com.android.wm.shell.R.dimen.bubble_size),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
similarity index 88%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
index 128f58b..66c9afb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
@@ -18,4 +18,4 @@
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+val Kosmos.activityTransitionAnimator by Kosmos.Fixture { ActivityTransitionAnimator() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
new file mode 100644
index 0000000..5485f79
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.app.admin.devicePolicyManager
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.settings.fakeSettings
+
+val Kosmos.communalSettingsRepository: CommunalSettingsRepository by
+    Kosmos.Fixture {
+        CommunalSettingsRepositoryImpl(
+            bgDispatcher = testDispatcher,
+            featureFlagsClassic = featureFlagsClassic,
+            secureSettings = fakeSettings,
+            broadcastDispatcher = broadcastDispatcher,
+            devicePolicyManager = devicePolicyManager,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index cccd908..ae7d877 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -16,7 +16,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class FakeCommunalRepository(
     applicationScope: CoroutineScope,
-    override var isCommunalEnabled: Boolean = true,
     override val desiredScene: MutableStateFlow<CommunalSceneKey> =
         MutableStateFlow(CommunalSceneKey.DEFAULT),
 ) : CommunalRepository {
@@ -40,21 +39,10 @@
         _transitionState.value = transitionState
     }
 
-    fun setIsCommunalEnabled(value: Boolean) {
-        isCommunalEnabled = value
-    }
-
     private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
     override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
 
     fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
         _isCommunalHubShowing.value = isCommunalHubShowing
     }
-
-    private val _communalEnabledState: MutableStateFlow<Boolean> = MutableStateFlow(false)
-    override val communalEnabledState: StateFlow<Boolean> = _communalEnabledState
-
-    fun setCommunalEnabledState(enabled: Boolean) {
-        _communalEnabledState.value = enabled
-    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index c47f020..f7e9a11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.smartspace.data.repository.smartspaceRepository
-import com.android.systemui.user.data.repository.userRepository
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.communalInteractor by Fixture {
@@ -38,12 +37,12 @@
         mediaRepository = communalMediaRepository,
         communalPrefsRepository = communalPrefsRepository,
         smartspaceRepository = smartspaceRepository,
-        userRepository = userRepository,
         appWidgetHost = mock(),
         keyguardInteractor = keyguardInteractor,
         editWidgetsActivityStarter = editWidgetsActivityStarter,
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
+        communalSettingsInteractor = communalSettingsInteractor,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
new file mode 100644
index 0000000..b4773f6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.communalSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.communalSettingsInteractor by Fixture {
+    CommunalSettingsInteractor(
+        bgScope = applicationCoroutineScope,
+        repository = communalSettingsRepository,
+        userInteractor = selectedUserInteractor,
+        tableLogBuffer = mock(),
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
index 9776b43..00fdced 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
@@ -31,6 +31,7 @@
             keyguardInteractor = keyguardInteractor,
             communalRepository = communalRepository,
             communalInteractor = communalInteractor,
+            communalSettingsInteractor = communalSettingsInteractor,
             tableLogBuffer = mock(),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
index 8042b5c..c83710a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
@@ -23,7 +23,7 @@
 import com.android.internal.logging.metricsLogger
 import com.android.internal.widget.lockPatternUtils
 import com.android.systemui.activityIntentHelper
-import com.android.systemui.animation.activityLaunchAnimator
+import com.android.systemui.animation.activityTransitionAnimator
 import com.android.systemui.assist.assistManager
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
@@ -78,7 +78,7 @@
             notificationPresenter,
             shadeViewController,
             notificationShadeWindowController,
-            activityLaunchAnimator,
+            activityTransitionAnimator,
             shadeAnimationInteractor,
             notificationLaunchAnimatorControllerProvider,
             launchFullScreenIntentProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index 128f58b..bcb5848 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.animation
+package com.android.systemui.util.settings
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+val Kosmos.fakeSettings: FakeSettings by Fixture { FakeSettings() }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a11cf8c..26c1bc9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -928,17 +928,11 @@
          * Dumps all {@link AccessibilityWindowInfo}s here.
          */
         void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) {
-            pw.append("Global Info [ ");
-            pw.println("Top focused display Id = " + mTopFocusedDisplayId);
-            pw.println("     Active Window Id = " + mActiveWindowId);
-            pw.println("     Top Focused Window Id = " + mTopFocusedWindowId);
-            pw.println("     Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
-                    + " ]");
             if (mIsProxy) {
                 pw.println("Proxy accessibility focused window = "
                         + mProxyDisplayAccessibilityFocusedWindow);
+                pw.println();
             }
-            pw.println();
             if (mWindows != null) {
                 final int windowCount = mWindows.size();
                 for (int j = 0; j < windowCount; j++) {
@@ -2201,6 +2195,13 @@
      * Dumps all {@link AccessibilityWindowInfo}s here.
      */
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+        pw.append("Global Info [ ");
+        pw.println("Top focused display Id = " + mTopFocusedDisplayId);
+        pw.println("     Active Window Id = " + mActiveWindowId);
+        pw.println("     Top Focused Window Id = " + mTopFocusedWindowId);
+        pw.println("     Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId
+                + " ]");
+        pw.println();
         final int count = mDisplayWindowsObservers.size();
         for (int i = 0; i < count; i++) {
             final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index fd8ab96..e1291e5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -451,7 +451,7 @@
 
         final Session.SaveResult saveResult = session.showSaveLocked();
 
-        session.logContextCommitted(saveResult.getNoSaveUiReason(), commitReason);
+        session.logContextCommittedLocked(saveResult.getNoSaveUiReason(), commitReason);
 
         if (saveResult.isLogSaveShown()) {
             session.logSaveUiShown();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2ff23ee..b89e0d8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3086,6 +3086,10 @@
      * when necessary.
      */
     public void logContextCommitted() {
+        if (sVerbose) {
+            Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN
+                    + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE);
+        }
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
                 Event.NO_SAVE_UI_REASON_NONE,
                 COMMIT_REASON_UNKNOWN));
@@ -3094,16 +3098,26 @@
 
     /**
      * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
-     * when necessary.
+     * when necessary. Note that it could be called before save UI is shown and the session is
+     * committed.
      *
      * @param saveDialogNotShowReason The reason why a save dialog was not shown.
      * @param commitReason The reason why context is committed.
      */
-    public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason,
+
+    @GuardedBy("mLock")
+    public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason,
             @AutofillCommitReason int commitReason) {
+        if (sVerbose) {
+            Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
+                    + " no_save_reason:" + saveDialogNotShowReason);
+        }
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
                 saveDialogNotShowReason, commitReason));
-        logAllEvents(commitReason);
+
+        mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
+        mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+        mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
     }
 
     private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
@@ -3159,6 +3173,10 @@
             @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
             @NoSaveReason int saveDialogNotShowReason,
             @AutofillCommitReason int commitReason) {
+        if (sVerbose) {
+            Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
+                    + " no_save_reason:" + saveDialogNotShowReason);
+        }
         final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
         if (lastResponse == null) return;
 
@@ -3335,7 +3353,9 @@
                 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
                 manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
                 mComponentName, mCompatMode, saveDialogNotShowReason);
-        logAllEvents(commitReason);
+        mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
+        mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+        mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
     }
 
     /**
@@ -3776,11 +3796,6 @@
                     }
                 }
 
-                if (sDebug) {
-                    Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
-                            + id + "!");
-                }
-
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(new Binder(), id, client);
 
@@ -3812,6 +3827,10 @@
                     }
                 }
                 mSessionFlags.mShowingSaveUi = true;
+                if (sDebug) {
+                    Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
+                            + id + "!");
+                }
                 return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
                         Event.NO_SAVE_UI_REASON_NONE);
             }
@@ -6386,6 +6405,9 @@
 
     @GuardedBy("mLock")
     private void logAllEvents(@AutofillCommitReason int val) {
+        if (sVerbose) {
+            Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
+        }
         mSessionCommittedEventLogger.maybeSetCommitReason(val);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
         mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
@@ -6411,6 +6433,9 @@
     @GuardedBy("mLock")
     RemoteFillService destroyLocked() {
         // Log unlogged events.
+        if (sVerbose) {
+            Slog.v(TAG, "destroyLocked for session: " + id);
+        }
         logAllEvents(COMMIT_REASON_SESSION_DESTROYED);
 
         if (mDestroyed) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0054bc8..b43f1a9 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1305,6 +1305,8 @@
             mAssociationStore.dump(out);
             mDevicePresenceMonitor.dump(out);
             mCompanionAppController.dump(out);
+            mTransportManager.dump(out);
+            mSystemDataTransferRequestStore.dump(out);
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 51c5fd6..c4c80f9 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -48,6 +48,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -303,6 +304,32 @@
         }
     }
 
+
+
+    /**
+     * Dumps current system data transfer request states.
+     */
+    public void dump(@NonNull PrintWriter out) {
+        synchronized (mLock) {
+            out.append("System Data Transfer Requests (Cached): ");
+            if (mCachedPerUser.size() == 0) {
+                out.append("<empty>\n");
+            } else {
+                out.append("\n");
+                for (int i = 0; i < mCachedPerUser.size(); i++) {
+                    final int userId = mCachedPerUser.keyAt(i);
+                    for (SystemDataTransferRequest request : mCachedPerUser.get(userId)) {
+                        out.append("  u")
+                                .append(String.valueOf(userId))
+                                .append(" -> ")
+                                .append(request.toString())
+                                .append('\n');
+                    }
+                }
+            }
+        }
+    }
+
     private void writeRequestsToXml(@NonNull TypedXmlSerializer serializer,
             @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
         serializer.startTag(null, XML_TAG_REQUESTS);
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 3e45626..3861f99 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -36,6 +36,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -225,6 +226,25 @@
     }
 
     /**
+     * Dumps current list of active transports.
+     */
+    public void dump(@NonNull PrintWriter out) {
+        synchronized (mTransports) {
+            out.append("System Data Transports: ");
+            if (mTransports.size() == 0) {
+                out.append("<empty>\n");
+            } else {
+                out.append("\n");
+                for (int i = 0; i < mTransports.size(); i++) {
+                    final int associationId = mTransports.keyAt(i);
+                    final Transport transport = mTransports.get(associationId);
+                    out.append("  ").append(transport.toString()).append('\n');
+                }
+            }
+        }
+    }
+
+    /**
      * @hide
      */
     public void enableSecureTransport(boolean enabled) {
diff --git a/services/companion/java/com/android/server/companion/transport/RawTransport.java b/services/companion/java/com/android/server/companion/transport/RawTransport.java
index ca169aac..05703ce 100644
--- a/services/companion/java/com/android/server/companion/transport/RawTransport.java
+++ b/services/companion/java/com/android/server/companion/transport/RawTransport.java
@@ -94,6 +94,13 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return "RawTransport{"
+                + "mAssociationId=" + mAssociationId
+                + '}';
+    }
+
     private void receiveMessage() throws IOException {
         synchronized (mRemoteIn) {
             final byte[] headerBytes = new byte[HEADER_LENGTH];
diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
index 6e906eb..1e95e65 100644
--- a/services/companion/java/com/android/server/companion/transport/SecureTransport.java
+++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
@@ -152,4 +152,12 @@
             close();
         }
     }
+
+    @Override
+    public String toString() {
+        return "SecureTransport{"
+                + "mAssociationId=" + mAssociationId
+                + ", mSecureChannel=" + mSecureChannel
+                + '}';
+    }
 }
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 627a62e..34c3d7e 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -246,16 +246,6 @@
 
         @Override
         public void run() {
-            if (mGuid.isEmpty()) {
-                Slog.e(TAG, "adbwifi guid was not set");
-                return;
-            }
-            mPort = native_pairing_start(mGuid, mPairingCode);
-            if (mPort <= 0 || mPort > 65535) {
-                Slog.e(TAG, "Unable to start pairing server");
-                return;
-            }
-
             // Register the mdns service
             NsdServiceInfo serviceInfo = new NsdServiceInfo();
             serviceInfo.setServiceName(mServiceName);
@@ -288,6 +278,28 @@
             mHandler.sendMessage(message);
         }
 
+        @Override
+        public void start() {
+            /*
+             * If a user is fast enough to click cancel, native_pairing_cancel can be invoked
+             * while native_pairing_start is running which run the destruction of the object
+             * while it is being constructed. Here we start the pairing server on foreground
+             * Thread so native_pairing_cancel can never be called concurrently. Then we let
+             * the pairing server run on a background Thread.
+             */
+            if (mGuid.isEmpty()) {
+                Slog.e(TAG, "adbwifi guid was not set");
+                return;
+            }
+            mPort = native_pairing_start(mGuid, mPairingCode);
+            if (mPort <= 0) {
+                Slog.e(TAG, "Unable to start pairing server");
+                return;
+            }
+
+            super.start();
+        }
+
         public void cancelPairing() {
             native_pairing_cancel();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a5531ae..2750344 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -187,6 +187,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.DUMP_VISIBLE_ACTIVITIES;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString;
+import static com.android.systemui.shared.Flags.enableHomeDelay;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -521,6 +522,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -921,6 +923,15 @@
     @GuardedBy("this")
     final ComponentAliasResolver mComponentAliasResolver;
 
+    private static final long HOME_LAUNCH_TIMEOUT_MS = 15000;
+    private final AtomicBoolean mHasHomeDelay = new AtomicBoolean(false);
+
+    /**
+     * Tracks all users with computed color resources by ThemeOverlaycvontroller
+     */
+    @GuardedBy("this")
+    private final Set<Integer> mThemeOverlayReadiness = new HashSet<>();
+
     /**
      * Tracks association information for a particular package along with debuggability.
      * <p> Associations for a package A are allowed to package B if B is part of the
@@ -2332,6 +2343,7 @@
                 mService.startBroadcastObservers();
             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 mService.mPackageWatchdog.onPackagesReady();
+                mService.setHomeTimeout();
             }
         }
 
@@ -5304,6 +5316,59 @@
         }
     }
 
+    /**
+     * Starts Home if there is no completion signal from ThemeOverlayController
+     */
+    private void setHomeTimeout() {
+        if (enableHomeDelay() && mHasHomeDelay.compareAndSet(false, true)) {
+            mHandler.postDelayed(() -> {
+                if (!getThemeOverlayReadiness()) {
+                    Slog.d(TAG,
+                            "ThemeHomeDelay: ThemeOverlayController not responding, launching "
+                                    + "Home after "
+                                    + HOME_LAUNCH_TIMEOUT_MS + "ms");
+                    setThemeOverlayReady(true);
+                }
+            }, HOME_LAUNCH_TIMEOUT_MS);
+        }
+    }
+
+    /**
+     * Used by ThemeOverlayController to notify all listeners for
+     * color palette readiness.
+     * @hide
+     */
+    @Override
+    public void setThemeOverlayReady(boolean readiness) {
+        enforceCallingPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY,
+                "setThemeOverlayReady");
+
+        int currentUserId = mUserController.getCurrentUserId();
+
+        boolean updateReadiness;
+        synchronized (mThemeOverlayReadiness) {
+            updateReadiness = readiness ? mThemeOverlayReadiness.add(currentUserId)
+                    : mThemeOverlayReadiness.remove(currentUserId);
+        }
+
+        if (updateReadiness && readiness && enableHomeDelay()) {
+            mAtmInternal.startHomeOnAllDisplays(currentUserId, "setThemeOverlayReady");
+        }
+    }
+
+    /**
+     * Returns current state of ThemeOverlayController color
+     * palette readiness.
+     *
+     * @hide
+     */
+    public boolean getThemeOverlayReadiness() {
+        int uid = mUserController.getCurrentUserId();
+        synchronized (mThemeOverlayReadiness) {
+            return mThemeOverlayReadiness.contains(uid);
+        }
+    }
+
     final void ensureBootCompleted() {
         boolean booting;
         boolean enableScreen;
@@ -18033,6 +18098,10 @@
             mAtmInternal.onUserStopped(userId);
             // Clean up various services by removing the user
             mBatteryStatsService.onUserRemoved(userId);
+
+            synchronized (mThemeOverlayReadiness) {
+                mThemeOverlayReadiness.remove(userId);
+            }
         }
 
         @Override
@@ -19391,6 +19460,11 @@
             return ActivityManagerService.this.clearApplicationUserData(packageName, keepState,
                     isRestore, observer, userId);
         }
+
+        @Override
+        public boolean getThemeOverlayReadiness() {
+            return ActivityManagerService.this.getThemeOverlayReadiness();
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bbbba26..04deb02 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -12458,6 +12458,20 @@
         return app;
     }
 
+    /**
+     * Retrieves all audioMixes registered with the AudioPolicyManager
+     * @return list of registered audio mixes
+     */
+    public List<AudioMix> getRegisteredPolicyMixes() {
+        if (!android.media.audiopolicy.Flags.audioMixTestApi()) {
+            return Collections.emptyList();
+        }
+
+        synchronized (mAudioPolicies) {
+            return mAudioSystem.getRegisteredPolicyMixes();
+        }
+    }
+
     public int addMixForPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb) {
         if (DEBUG_AP) { Log.d(TAG, "addMixForPolicy for " + pcb.asBinder()
                 + " with config:" + policyConfig); }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 4f46dd1..49ab19a 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -27,6 +27,7 @@
 import android.media.ISoundDoseCallback;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioMixingRule;
+import android.media.audiopolicy.Flags;
 import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -42,6 +43,7 @@
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -602,6 +604,23 @@
     }
 
     /**
+     * @return a list of AudioMixes that are registered in the audio policy manager.
+     */
+    public List<AudioMix> getRegisteredPolicyMixes() {
+        if (!Flags.audioMixTestApi()) {
+            return Collections.emptyList();
+        }
+
+        List<AudioMix> audioMixes = new ArrayList<>();
+        int result = AudioSystem.getRegisteredPolicyMixes(audioMixes);
+        if (result != AudioSystem.SUCCESS) {
+            throw new IllegalStateException(
+                    "Cannot fetch registered policy mixes. Result: " + result);
+        }
+        return Collections.unmodifiableList(audioMixes);
+    }
+
+    /**
      * Update already {@link AudioMixingRule}-s for already registered {@link AudioMix}-es.
      *
      * @param mixes              - array of registered {@link AudioMix}-es to update.
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index fbd32a6..d061e2d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -31,6 +31,7 @@
 
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
 
 import java.util.function.Supplier;
 
@@ -202,6 +203,16 @@
         }
     }
 
+    // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
+    protected final void resetIgnoreDisplayTouches() {
+        final AidlSession session = (AidlSession) getFreshDaemon();
+        try {
+            session.getSession().setIgnoreDisplayTouches(false);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when resetting setIgnoreDisplayTouches");
+        }
+    }
+
     @Override
     public boolean isInterruptable() {
         return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
index 92218b1..199db8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -27,9 +27,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * Allows clients (such as keyguard) to register for notifications on when biometric lockout
@@ -42,7 +41,7 @@
 
     private final Context mContext;
     @VisibleForTesting
-    final List<ClientCallback> mClientCallbacks = new ArrayList<>();
+    final ConcurrentLinkedQueue<ClientCallback> mClientCallbacks = new ConcurrentLinkedQueue<>();
 
     private static class ClientCallback {
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 8121a63..93d1b6e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -232,6 +232,7 @@
         handleLockout(authenticated);
         if (authenticated) {
             mState = STATE_STOPPED;
+            resetIgnoreDisplayTouches();
             mSensorOverlays.hide(getSensorId());
             if (sidefpsControllerRefactor()) {
                 mAuthenticationStateListeners.onAuthenticationStopped();
@@ -268,6 +269,7 @@
                 // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
                 // controlled by the HAL, the framework must stop the sensor before finishing the
                 // client.
+                resetIgnoreDisplayTouches();
                 mSensorOverlays.hide(getSensorId());
                 if (sidefpsControllerRefactor()) {
                     mAuthenticationStateListeners.onAuthenticationStopped();
@@ -298,6 +300,7 @@
             BiometricNotificationUtils.showBadCalibrationNotification(getContext());
         }
 
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
@@ -306,6 +309,7 @@
 
     @Override
     protected void startHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.show(getSensorId(), getRequestReason(), this);
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStarted(getRequestReason());
@@ -419,6 +423,7 @@
 
     @Override
     protected void stopHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
@@ -518,6 +523,7 @@
             Slog.e(TAG, "Remote exception", e);
         }
 
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
@@ -548,6 +554,7 @@
             Slog.e(TAG, "Remote exception", e);
         }
 
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index cb220b9e..8d2b46f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -87,6 +87,7 @@
 
     @Override
     protected void stopHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         unsubscribeBiometricContext();
 
@@ -102,6 +103,7 @@
 
     @Override
     protected void startHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
                 this);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 225bd59..79975e5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -144,6 +144,7 @@
                 controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
 
         if (remaining == 0) {
+            resetIgnoreDisplayTouches();
             mSensorOverlays.hide(getSensorId());
             if (sidefpsControllerRefactor()) {
                 mAuthenticationStateListeners.onAuthenticationStopped();
@@ -178,6 +179,7 @@
     @Override
     public void onError(int errorCode, int vendorCode) {
         super.onError(errorCode, vendorCode);
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
@@ -192,6 +194,7 @@
 
     @Override
     protected void startHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.show(getSensorId(), getRequestReasonFromEnrollReason(mEnrollReason),
                 this);
         if (sidefpsControllerRefactor()) {
@@ -273,6 +276,7 @@
 
     @Override
     protected void stopHalOperation() {
+        resetIgnoreDisplayTouches();
         mSensorOverlays.hide(getSensorId());
         if (sidefpsControllerRefactor()) {
             mAuthenticationStateListeners.onAuthenticationStopped();
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index c260f10..6a6e6ab 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -47,5 +47,19 @@
      * @see Configuration#getGrammaticalGender
      */
     public abstract @Configuration.GrammaticalGender int getSystemGrammaticalGender(int userId);
+
+    /**
+     * Retrieve the system grammatical gender.
+     *
+     * @return the value of grammatical gender
+     *
+     */
+    public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
+            Configuration configuration);
+
+    /**
+     * Whether the package can get the system grammatical gender or not.
+     */
+    public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName);
 }
 
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 6eb7e95..d01f54f 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -22,17 +22,21 @@
 import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
 
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.app.GrammaticalInflectionManager;
 import android.app.IGrammaticalInflectionManager;
 import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.permission.PermissionManager;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -43,6 +47,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -71,6 +76,7 @@
     private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
     private static final String GRAMMATICAL_INFLECTION_ENABLED =
             "i18n.grammatical_Inflection.enabled";
+    private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
 
     private final GrammaticalInflectionBackupHelper mBackupHelper;
     private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -121,16 +127,16 @@
         @Override
         public void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
             checkCallerIsSystem();
-            checkSystemTermsOfAddressIsEnabled();
             GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
                     userId);
         }
 
         @Override
         public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
-            checkSystemTermsOfAddressIsEnabled();
-            return GrammaticalInflectionService.this.getSystemGrammaticalGender(attributionSource,
-                    userId);
+            return canGetSystemGrammaticalGender(attributionSource)
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+                    attributionSource, userId)
+                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
         @Override
@@ -159,9 +165,33 @@
 
         @Override
         public int getSystemGrammaticalGender(int userId) {
-            checkCallerIsSystem();
-            return GrammaticalInflectionService.this.getSystemGrammaticalGender(
-                    mContext.getAttributionSource(), userId);
+            return checkSystemTermsOfAddressIsEnabled()
+                    ? GrammaticalInflectionService.this.getSystemGrammaticalGender(
+                    mContext.getAttributionSource(), userId)
+                    : GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        }
+
+        @Override
+        public int retrieveSystemGrammaticalGender(Configuration configuration) {
+            int systemGrammaticalGender = getSystemGrammaticalGender(mContext.getUserId());
+            // Retrieve the grammatical gender from system property, set it into
+            // configuration which will get updated later if the grammatical gender raw value of
+            // current configuration is {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
+            if (configuration.getGrammaticalGenderRaw()
+                    == Configuration.GRAMMATICAL_GENDER_UNDEFINED
+                    || systemGrammaticalGender <= Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+                systemGrammaticalGender = SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
+                        Configuration.GRAMMATICAL_GENDER_UNDEFINED);
+            }
+            return systemGrammaticalGender;
+        }
+
+        @Override
+        public boolean canGetSystemGrammaticalGender(int uid, String packageName) {
+            AttributionSource attributionSource = new AttributionSource.Builder(
+                    uid).setPackageName(packageName).build();
+            return GrammaticalInflectionService.this.canGetSystemGrammaticalGender(
+                    attributionSource);
         }
     }
 
@@ -202,11 +232,20 @@
     }
 
     protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+        Trace.beginSection("GrammaticalInflectionService.setSystemWideGrammaticalGender");
         if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
                 grammaticalGender)) {
             throw new IllegalArgumentException("Unknown grammatical gender");
         }
 
+        if (!checkSystemTermsOfAddressIsEnabled()) {
+            if (grammaticalGender == GRAMMATICAL_GENDER_NOT_SPECIFIED) {
+                return;
+            }
+            Log.d(TAG, "Clearing the system grammatical gender setting");
+            grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+        }
+
         synchronized (mLock) {
             final File file = getGrammaticalGenderFile(userId);
             final AtomicFile atomicFile = new AtomicFile(file);
@@ -224,6 +263,15 @@
                 throw new RuntimeException(e);
             }
         }
+
+        try {
+            Configuration config = new Configuration();
+            config.setGrammaticalGender(grammaticalGender);
+            ActivityTaskManager.getService().updateConfiguration(config);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Can not update configuration", e);
+        }
+        Trace.endSection();
     }
 
     public int getSystemGrammaticalGender(AttributionSource attributionSource, int userId) {
@@ -233,34 +281,9 @@
             return GRAMMATICAL_GENDER_NOT_SPECIFIED;
         }
 
-        int callingUid = Binder.getCallingUid();
-        if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) != callingUid) {
-            Log.d(TAG,
-                    "Package " + packageName + " does not belong to the calling uid " + callingUid);
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
-
-        if (!checkSystemGrammaticalGenderPermission(mPermissionManager, attributionSource)) {
-            return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-        }
-
         synchronized (mLock) {
-            final File file = getGrammaticalGenderFile(userId);
-            if (!file.exists()) {
-                Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
-                return GRAMMATICAL_GENDER_NOT_SPECIFIED;
-            }
-
-            if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
-                try {
-                    InputStream in = new FileInputStream(file);
-                    final TypedXmlPullParser parser = Xml.resolvePullParser(in);
-                    mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
-                } catch (IOException | XmlPullParserException e) {
-                    Log.e(TAG, "Failed to parse XML configuration from " + file, e);
-                }
-            }
-            return mGrammaticalGenderCache.get(userId);
+            int grammaticalGender = mGrammaticalGenderCache.get(userId);
+            return grammaticalGender < 0 ? GRAMMATICAL_GENDER_NOT_SPECIFIED : grammaticalGender;
         }
     }
 
@@ -311,9 +334,39 @@
         }
     }
 
-    private void checkSystemTermsOfAddressIsEnabled() {
+    private boolean checkSystemTermsOfAddressIsEnabled() {
         if (!systemTermsOfAddressEnabled()) {
-            throw new RuntimeException("The flag must be enabled to allow calling the API.");
+            Log.d(TAG, "The flag must be enabled to allow calling the API.");
+            return false;
         }
+        return true;
+    }
+
+    private boolean canGetSystemGrammaticalGender(AttributionSource attributionSource) {
+        return checkSystemTermsOfAddressIsEnabled() && checkSystemGrammaticalGenderPermission(
+                mPermissionManager, attributionSource);
+    }
+
+    @Override
+    public void onUserUnlocked(TargetUser user) {
+        IoThread.getHandler().post(() -> {
+            int userId = user.getUserIdentifier();
+            final File file = getGrammaticalGenderFile(userId);
+            synchronized (mLock) {
+                if (!file.exists()) {
+                    Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+                    return;
+                }
+                if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+                    try {
+                        InputStream in = new FileInputStream(file);
+                        final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                        mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+                    } catch (IOException | XmlPullParserException e) {
+                        Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+                    }
+                }
+            }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e0e825d..c8c66238 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1366,6 +1366,12 @@
                                     // we don't call onInitializeCecComplete()
                                     // since we reallocate the logical address only.
                                     onInitializeCecComplete(initiatedBy);
+                                } else if (initiatedBy == INITIATED_BY_HOTPLUG
+                                        && mDisplayStatusCallback == null) {
+                                    // Force to update display status for hotplug event.
+                                    synchronized (mLock) {
+                                        announceHdmiControlStatusChange(mHdmiControlEnabled);
+                                    }
                                 }
                                 // We remove local devices here, instead of before the start of
                                 // address allocation, to prevent multiple local devices of the
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7726609..574be34 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -621,10 +621,6 @@
         mBatteryController.systemRunning();
         mKeyboardBacklightController.systemRunning();
         mKeyRemapper.systemRunning();
-
-        mNative.setStylusPointerIconEnabled(
-                Objects.requireNonNull(mContext.getSystemService(InputManager.class))
-                        .isStylusPointerIconEnabled());
     }
 
     private void reloadDeviceAliases() {
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 5ffc380..c02d524 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -94,7 +94,9 @@
                 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SLOW_KEYS),
                         (reason) -> updateAccessibilitySlowKeys()),
                 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
-                        (reason) -> updateAccessibilityStickyKeys()));
+                        (reason) -> updateAccessibilityStickyKeys()),
+                Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED),
+                        (reason) -> updateStylusPointerIconEnabled()));
     }
 
     /**
@@ -254,4 +256,8 @@
             mNative.setMinTimeBetweenUserActivityPokes(intervalMillis);
         }
     }
+
+    private void updateStylusPointerIconEnabled() {
+        mNative.setStylusPointerIconEnabled(InputSettings.isStylusPointerIconEnabled(mContext));
+    }
 }
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
new file mode 100644
index 0000000..1553618
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.NonNull;
+import android.app.BackgroundInstallControlManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IRemoteCallback;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+public class BackgroundInstallControlCallbackHelper {
+
+    @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+    @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId";
+    private static final String TAG = "BackgroundInstallControlCallbackHelper";
+
+    private final Handler mHandler;
+
+    BackgroundInstallControlCallbackHelper() {
+        HandlerThread backgroundThread =
+                new ServiceThread(
+                        "BackgroundInstallControlCallbackHelperBg",
+                        THREAD_PRIORITY_BACKGROUND,
+                        true);
+        backgroundThread.start();
+        mHandler = new Handler(backgroundThread.getLooper());
+    }
+
+    @NonNull @VisibleForTesting
+    final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
+
+    /** Registers callback that gets invoked upon detection of an MBA
+     *
+     * NOTE: The callback is user context agnostic and currently broadcasts to all users of other
+     * users app installs. This is fine because the API is for SystemServer use only.
+     */
+    public void registerBackgroundInstallCallback(IRemoteCallback callback) {
+        synchronized (mCallbacks) {
+            mCallbacks.register(callback, null);
+        }
+    }
+
+    /** Unregisters callback */
+    public void unregisterBackgroundInstallCallback(IRemoteCallback callback) {
+        synchronized (mCallbacks) {
+            mCallbacks.unregister(callback);
+        }
+    }
+
+    /**
+     * Invokes all registered callbacks Callbacks are processed through user provided-threads and
+     * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent
+     */
+    public void notifyAllCallbacks(int userId, String packageName) {
+        Bundle extras = new Bundle();
+        extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName);
+        extras.putInt(FLAGGED_USER_ID_KEY, userId);
+        synchronized (mCallbacks) {
+            mHandler.post(
+                    () ->
+                            mCallbacks.broadcast(
+                                    callback -> {
+                                        try {
+                                            callback.sendResult(extras);
+                                        } catch (RemoteException e) {
+                                            Slog.e(
+                                                    TAG,
+                                                    "error detected: " + e.getLocalizedMessage(),
+                                                    e);
+                                        }
+                                    }));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 200b17b..3468081 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
@@ -93,6 +94,8 @@
     private final File mDiskFile;
     private final Context mContext;
 
+    private final BackgroundInstallControlCallbackHelper mCallbackHelper;
+
     private SparseSetArray<String> mBackgroundInstalledPackages = null;
 
     // User ID -> package name -> set of foreground time frame
@@ -112,6 +115,7 @@
         mHandler = new EventHandler(injector.getLooper(), this);
         mDiskFile = injector.getDiskFile();
         mContext = injector.getContext();
+        mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper();
         UsageStatsManagerInternal usageStatsManagerInternal =
                 injector.getUsageStatsManagerInternal();
         usageStatsManagerInternal.registerListener(
@@ -150,6 +154,16 @@
             }
         }
 
+        @Override
+        public void registerBackgroundInstallCallback(IRemoteCallback callback) {
+            mService.mCallbackHelper.registerBackgroundInstallCallback(callback);
+        }
+
+        @Override
+        public void unregisterBackgroundInstallCallback(IRemoteCallback callback) {
+            mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback);
+        }
+
     }
 
     @RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES)
@@ -274,6 +288,7 @@
 
         initBackgroundInstalledPackages();
         mBackgroundInstalledPackages.add(userId, packageName);
+        mCallbackHelper.notifyAllCallbacks(userId, packageName);
         writeBackgroundInstalledPackagesToDisk();
     }
 
@@ -568,6 +583,8 @@
 
         File getDiskFile();
 
+        BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper();
+
     }
 
     private static final class InjectorImpl implements Injector {
@@ -617,5 +634,10 @@
             File file = new File(dir, DISK_FILE_NAME);
             return file;
         }
+
+        @Override
+        public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() {
+            return new BackgroundInstallControlCallbackHelper();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index a9e5a54..1c70af0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -594,6 +594,7 @@
         }
         ai.applicationInfo = applicationInfo;
         ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
+        ai.requireContentUriPermissionFromCaller = a.getRequireContentUriPermissionFromCaller();
         ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
         assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
         return ai;
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
index 03c75e0..195e91c 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
+import android.content.pm.ActivityInfo.RequiredContentUriPermission;
 import android.content.pm.ProviderInfo;
 import android.net.Uri;
 import android.os.IBinder;
@@ -63,6 +64,15 @@
             String targetPkg, int targetUserId);
 
     /**
+     * Same as {@link #checkGrantUriPermissionFromIntent(Intent, int, String, int)}, but with an
+     * extra parameter {@code requireContentUriPermissionFromCaller}, which is the value from {@link
+     * android.R.attr#requireContentUriPermissionFromCaller} attribute.
+     */
+    NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid,
+            String targetPkg, int targetUserId,
+            @RequiredContentUriPermission int requireContentUriPermissionFromCaller);
+
+    /**
      * Extend a previously calculated set of permissions grants to the given
      * owner. All security checks will have already been performed as part of
      * calculating {@link NeededUriGrants}.
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index ce2cbed..d2f6701 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -23,6 +23,10 @@
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
 import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
+import static android.content.pm.ActivityInfo.CONTENT_URI_PERMISSION_NONE;
+import static android.content.pm.ActivityInfo.CONTENT_URI_PERMISSION_READ_OR_WRITE;
+import static android.content.pm.ActivityInfo.isRequiredContentUriPermissionRead;
+import static android.content.pm.ActivityInfo.isRequiredContentUriPermissionWrite;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
@@ -53,6 +57,8 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.RequiredContentUriPermission;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -609,7 +615,8 @@
 
     /** Like checkGrantUriPermission, but takes an Intent. */
     private NeededUriGrants checkGrantUriPermissionFromIntentUnlocked(int callingUid,
-            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId,
+            @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller) {
         if (DEBUG) Slog.v(TAG,
                 "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
                         + " clip=" + (intent != null ? intent.getClipData() : null)
@@ -647,6 +654,10 @@
         }
         if (data != null) {
             GrantUri grantUri = GrantUri.resolve(contentUserHint, data, mode);
+            if (android.security.Flags.contentUriPermissionApis()) {
+                enforceRequireContentUriPermissionFromCaller(requireContentUriPermissionFromCaller,
+                        grantUri, callingUid);
+            }
             targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, mode,
                     targetUid);
             if (targetUid > 0) {
@@ -661,6 +672,10 @@
                 Uri uri = clip.getItemAt(i).getUri();
                 if (uri != null) {
                     GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode);
+                    if (android.security.Flags.contentUriPermissionApis()) {
+                        enforceRequireContentUriPermissionFromCaller(
+                                requireContentUriPermissionFromCaller, grantUri, callingUid);
+                    }
                     targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg,
                             grantUri, mode, targetUid);
                     if (targetUid > 0) {
@@ -673,7 +688,8 @@
                     Intent clipIntent = clip.getItemAt(i).getIntent();
                     if (clipIntent != null) {
                         NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentUnlocked(
-                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
+                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId,
+                                requireContentUriPermissionFromCaller);
                         if (newNeeded != null) {
                             needed = newNeeded;
                         }
@@ -685,6 +701,38 @@
         return needed;
     }
 
+    private void enforceRequireContentUriPermissionFromCaller(
+            @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller,
+            GrantUri grantUri, int uid) {
+        // Ignore if requireContentUriPermissionFromCaller hasn't been set or the URI is a
+        // non-content URI.
+        if (requireContentUriPermissionFromCaller == null
+                || requireContentUriPermissionFromCaller == CONTENT_URI_PERMISSION_NONE
+                || !ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
+            return;
+        }
+
+        final boolean readMet = !isRequiredContentUriPermissionRead(
+                requireContentUriPermissionFromCaller)
+                || checkContentUriPermissionFullUnlocked(grantUri, uid,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        final boolean writeMet = !isRequiredContentUriPermissionWrite(
+                requireContentUriPermissionFromCaller)
+                || checkContentUriPermissionFullUnlocked(grantUri, uid,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        boolean hasPermission =
+                requireContentUriPermissionFromCaller == CONTENT_URI_PERMISSION_READ_OR_WRITE
+                        ? (readMet || writeMet) : (readMet && writeMet);
+
+        if (!hasPermission) {
+            throw new SecurityException("You can't launch this activity because you don't have the"
+                    + " required " + ActivityInfo.requiredContentUriPermissionToShortString(
+                            requireContentUriPermissionFromCaller) + " access to " + grantUri.uri);
+        }
+    }
+
     @GuardedBy("mLock")
     private void readGrantedUriPermissionsLocked() {
         if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()");
@@ -1560,9 +1608,24 @@
         @Override
         public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid,
                 String targetPkg, int targetUserId) {
+            return internalCheckGrantUriPermissionFromIntent(intent, callingUid, targetPkg,
+                    targetUserId, /* requireContentUriPermissionFromCaller */ null);
+        }
+
+        @Override
+        public NeededUriGrants checkGrantUriPermissionFromIntent(Intent intent, int callingUid,
+                String targetPkg, int targetUserId, int requireContentUriPermissionFromCaller) {
+            return internalCheckGrantUriPermissionFromIntent(intent, callingUid, targetPkg,
+                    targetUserId, requireContentUriPermissionFromCaller);
+        }
+
+        private NeededUriGrants internalCheckGrantUriPermissionFromIntent(Intent intent,
+                int callingUid, String targetPkg, int targetUserId,
+                @Nullable Integer requireContentUriPermissionFromCaller) {
             final int mode = (intent != null) ? intent.getFlags() : 0;
             return UriGrantsManagerService.this.checkGrantUriPermissionFromIntentUnlocked(
-                    callingUid, targetPkg, intent, mode, null, targetUserId);
+                    callingUid, targetPkg, intent, mode, null, targetUserId,
+                    requireContentUriPermissionFromCaller);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index 8f8fe3c..17a9e33 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -248,8 +248,6 @@
             IVibratorController vibratorController =
                     mVibratorControllerHolder.getVibratorController();
             if (vibratorController == null) {
-                Slog.d(TAG, "Unable to check if should request vibration params. "
-                        + "There is no registered IVibrationController.");
                 return false;
             }
 
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
index e1abae8..88d3daf 100644
--- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -118,4 +118,62 @@
         }
         post(service -> service.provideData(data, sharedMemory, callback));
     }
+
+    /**
+     * Registers a data request observer with WearableSensingService.
+     *
+     * @param dataType The data type to listen to. Values are defined by the application that
+     *     implements WearableSensingService.
+     * @param dataRequestCallback The observer to send data requests to.
+     * @param dataRequestObserverId The unique ID for the data request observer. It will be used for
+     *     unregistering the observer.
+     * @param packageName The package name of the app that will receive the data requests.
+     * @param statusCallback The callback for status of the method call.
+     */
+    public void registerDataRequestObserver(
+            int dataType,
+            RemoteCallback dataRequestCallback,
+            int dataRequestObserverId,
+            String packageName,
+            RemoteCallback statusCallback) {
+        if (DEBUG) {
+            Slog.i(TAG, "Registering data request observer.");
+        }
+        var unused =
+                post(
+                        service ->
+                                service.registerDataRequestObserver(
+                                        dataType,
+                                        dataRequestCallback,
+                                        dataRequestObserverId,
+                                        packageName,
+                                        statusCallback));
+    }
+
+    /**
+     * Unregisters a previously registered data request observer.
+     *
+     * @param dataType The data type the observer was registered against.
+     * @param dataRequestObserverId The unique ID of the observer to unregister.
+     * @param packageName The package name of the app that will receive requests sent to the
+     *     observer.
+     * @param statusCallback The callback for status of the method call.
+     */
+    public void unregisterDataRequestObserver(
+            int dataType,
+            int dataRequestObserverId,
+            String packageName,
+            RemoteCallback statusCallback) {
+        if (DEBUG) {
+            Slog.i(TAG, "Unregistering data request observer.");
+        }
+        var unused =
+                post(
+                        service ->
+                                service.unregisterDataRequestObserver(
+                                        dataType,
+                                        dataRequestObserverId,
+                                        packageName,
+                                        statusCallback));
+    }
 }
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index a8d6322..0e8b82f 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -262,4 +262,65 @@
             mRemoteService.provideData(data, sharedMemory, callback);
         }
     }
+
+    /**
+     * Handles registering a data request observer.
+     *
+     * @param dataType The data type to listen to. Values are defined by the application that
+     *     implements WearableSensingService.
+     * @param dataRequestObserver The observer to register.
+     * @param dataRequestObserverId The unique ID for the data request observer. It will be used for
+     *     unregistering the observer.
+     * @param packageName The package name of the app that will receive the data requests.
+     * @param statusCallback The callback for status of the method call.
+     */
+    public void onRegisterDataRequestObserver(
+            int dataType,
+            RemoteCallback dataRequestObserver,
+            int dataRequestObserverId,
+            String packageName,
+            RemoteCallback statusCallback) {
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            ensureRemoteServiceInitiated();
+            mRemoteService.registerDataRequestObserver(
+                    dataType,
+                    dataRequestObserver,
+                    dataRequestObserverId,
+                    packageName,
+                    statusCallback);
+        }
+    }
+
+    /**
+     * Handles unregistering a previously registered data request observer.
+     *
+     * @param dataType The data type the observer was registered against.
+     * @param dataRequestObserverId The unique ID of the observer to unregister.
+     * @param packageName The package name of the app that will receive requests sent to the
+     *     observer.
+     * @param statusCallback The callback for status of the method call.
+     */
+    public void onUnregisterDataRequestObserver(
+            int dataType,
+            int dataRequestObserverId,
+            String packageName,
+            RemoteCallback statusCallback) {
+        synchronized (mLock) {
+            if (!setUpServiceIfNeeded()) {
+                Slog.w(TAG, "Detection service is not available at this moment.");
+                notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            ensureRemoteServiceInitiated();
+            mRemoteService.unregisterDataRequestObserver(
+                    dataType, dataRequestObserverId, packageName, statusCallback);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 28c8f87..78952fa 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -21,12 +21,18 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.app.BroadcastOptions;
+import android.app.ComponentOptions;
+import android.app.PendingIntent;
 import android.app.ambientcontext.AmbientContextEvent;
 import android.app.wearable.IWearableSensingManager;
+import android.app.wearable.WearableSensingDataRequest;
 import android.app.wearable.WearableSensingManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
@@ -35,6 +41,8 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.service.wearable.WearableSensingDataRequester;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -44,10 +52,13 @@
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
 import com.android.server.pm.KnownPackages;
+import com.android.server.utils.quota.MultiRateLimiter;
 
 import java.io.FileDescriptor;
+import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 /**
@@ -64,9 +75,38 @@
 
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
     public static final int MAX_TEMPORARY_SERVICE_DURATION_MS = 30000;
 
+    private static final String RATE_LIMITER_PACKAGE_NAME = "android";
+    private static final String RATE_LIMITER_TAG =
+            WearableSensingManagerService.class.getSimpleName();
+
+    private static final class DataRequestObserverContext {
+        final int mDataType;
+        final int mUserId;
+        final int mDataRequestObserverId;
+        @NonNull final PendingIntent mDataRequestPendingIntent;
+        @NonNull final RemoteCallback mDataRequestRemoteCallback;
+
+        DataRequestObserverContext(
+                int dataType,
+                int userId,
+                int dataRequestObserverId,
+                PendingIntent dataRequestPendingIntent,
+                RemoteCallback dataRequestRemoteCallback) {
+            mDataType = dataType;
+            mUserId = userId;
+            mDataRequestObserverId = dataRequestObserverId;
+            mDataRequestPendingIntent = dataRequestPendingIntent;
+            mDataRequestRemoteCallback = dataRequestRemoteCallback;
+        }
+    }
+
     private final Context mContext;
+    private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1);
+    private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>();
+    private final MultiRateLimiter mDataRequestRateLimiter;
     volatile boolean mIsServiceEnabled;
 
     public WearableSensingManagerService(Context context) {
@@ -78,6 +118,12 @@
                 PACKAGE_UPDATE_POLICY_REFRESH_EAGER
                         | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
         mContext = context;
+        mDataRequestRateLimiter =
+                new MultiRateLimiter.Builder(context)
+                        .addRateLimit(
+                                WearableSensingDataRequest.getRateLimit(),
+                                WearableSensingDataRequest.getRateLimitWindowSize())
+                        .build();
     }
 
     @Override
@@ -192,6 +238,96 @@
         }
     }
 
+    private DataRequestObserverContext getDataRequestObserverContext(
+            int dataType, int userId, PendingIntent dataRequestPendingIntent) {
+        synchronized (mDataRequestObserverContexts) {
+            for (DataRequestObserverContext observerContext : mDataRequestObserverContexts) {
+                if (observerContext.mDataType == dataType
+                        && observerContext.mUserId == userId
+                        && observerContext.mDataRequestPendingIntent.equals(
+                                dataRequestPendingIntent)) {
+                    return observerContext;
+                }
+            }
+        }
+        return null;
+    }
+
+    @NonNull
+    private RemoteCallback createDataRequestRemoteCallback(
+            PendingIntent dataRequestPendingIntent, int userId) {
+        return new RemoteCallback(
+                bundle -> {
+                    WearableSensingDataRequest dataRequest =
+                            bundle.getParcelable(
+                                    WearableSensingDataRequest.REQUEST_BUNDLE_KEY,
+                                    WearableSensingDataRequest.class);
+                    if (dataRequest == null) {
+                        Slog.e(TAG, "Received data request callback without a request.");
+                        return;
+                    }
+                    RemoteCallback dataRequestStatusCallback =
+                            bundle.getParcelable(
+                                    WearableSensingDataRequest.REQUEST_STATUS_CALLBACK_BUNDLE_KEY,
+                                    RemoteCallback.class);
+                    if (dataRequestStatusCallback == null) {
+                        Slog.e(TAG, "Received data request callback without a status callback.");
+                        return;
+                    }
+                    if (dataRequest.getDataSize()
+                            > WearableSensingDataRequest.getMaxRequestSize()) {
+                        Slog.w(
+                                TAG,
+                                TextUtils.formatSimple(
+                                        "WearableSensingDataRequest size exceeds the maximum"
+                                            + " allowed size of %s bytes. Dropping the request.",
+                                        WearableSensingDataRequest.getMaxRequestSize()));
+                        WearableSensingManagerPerUserService.notifyStatusCallback(
+                                dataRequestStatusCallback,
+                                WearableSensingDataRequester.STATUS_TOO_LARGE);
+                        return;
+                    }
+                    if (!mDataRequestRateLimiter.isWithinQuota(
+                            userId, RATE_LIMITER_PACKAGE_NAME, RATE_LIMITER_TAG)) {
+                        Slog.w(TAG, "Data request exceeded rate limit. Dropping the request.");
+                        WearableSensingManagerPerUserService.notifyStatusCallback(
+                                dataRequestStatusCallback,
+                                WearableSensingDataRequester.STATUS_TOO_FREQUENT);
+                        return;
+                    }
+                    Intent intent = new Intent();
+                    intent.putExtra(
+                            WearableSensingManager.EXTRA_WEARABLE_SENSING_DATA_REQUEST,
+                            dataRequest);
+                    BroadcastOptions options = BroadcastOptions.makeBasic();
+                    options.setPendingIntentBackgroundActivityStartMode(
+                            ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
+                    mDataRequestRateLimiter.noteEvent(
+                            userId, RATE_LIMITER_PACKAGE_NAME, RATE_LIMITER_TAG);
+                    final long previousCallingIdentity = Binder.clearCallingIdentity();
+                    try {
+                        dataRequestPendingIntent.send(
+                                getContext(), 0, intent, null, null, null, options.toBundle());
+                        WearableSensingManagerPerUserService.notifyStatusCallback(
+                                dataRequestStatusCallback,
+                                WearableSensingDataRequester.STATUS_SUCCESS);
+                        Slog.i(
+                                TAG,
+                                TextUtils.formatSimple(
+                                        "Sending data request to %s: %s",
+                                        dataRequestPendingIntent.getCreatorPackage(),
+                                        dataRequest.toExpandedString()));
+                    } catch (PendingIntent.CanceledException e) {
+                        Slog.w(TAG, "Could not deliver pendingIntent: " + dataRequestPendingIntent);
+                        WearableSensingManagerPerUserService.notifyStatusCallback(
+                                dataRequestStatusCallback,
+                                WearableSensingDataRequester.STATUS_OBSERVER_CANCELLED);
+                    } finally {
+                        Binder.restoreCallingIdentity(previousCallingIdentity);
+                    }
+                });
+    }
+
     private void callPerUserServiceIfExist(
             Consumer<WearableSensingManagerPerUserService> serviceConsumer,
             RemoteCallback statusCallback) {
@@ -260,8 +396,8 @@
                     Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
             if (!mIsServiceEnabled) {
                 Slog.w(TAG, "Service not available.");
-                WearableSensingManagerPerUserService.notifyStatusCallback(callback,
-                        WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                WearableSensingManagerPerUserService.notifyStatusCallback(
+                        callback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
                 return;
             }
             callPerUserServiceIfExist(
@@ -270,6 +406,96 @@
         }
 
         @Override
+        public void registerDataRequestObserver(
+                int dataType,
+                PendingIntent dataRequestPendingIntent,
+                RemoteCallback statusCallback) {
+            Slog.i(TAG, "WearableSensingManagerInternal registerDataRequestObserver.");
+            Objects.requireNonNull(dataRequestPendingIntent);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                WearableSensingManagerPerUserService.notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            int userId = UserHandle.getCallingUserId();
+            RemoteCallback dataRequestCallback;
+            int dataRequestObserverId;
+            synchronized (mDataRequestObserverContexts) {
+                DataRequestObserverContext previousObserverContext =
+                        getDataRequestObserverContext(dataType, userId, dataRequestPendingIntent);
+                if (previousObserverContext != null) {
+                    Slog.i(TAG, "Received duplicate data request observer.");
+                    dataRequestCallback = previousObserverContext.mDataRequestRemoteCallback;
+                    dataRequestObserverId = previousObserverContext.mDataRequestObserverId;
+                } else {
+                    dataRequestCallback =
+                            createDataRequestRemoteCallback(dataRequestPendingIntent, userId);
+                    dataRequestObserverId = mNextDataRequestObserverId.getAndIncrement();
+                    mDataRequestObserverContexts.add(
+                            new DataRequestObserverContext(
+                                    dataType,
+                                    userId,
+                                    dataRequestObserverId,
+                                    dataRequestPendingIntent,
+                                    dataRequestCallback));
+                }
+            }
+            callPerUserServiceIfExist(
+                    service ->
+                            service.onRegisterDataRequestObserver(
+                                    dataType,
+                                    dataRequestCallback,
+                                    dataRequestObserverId,
+                                    dataRequestPendingIntent.getCreatorPackage(),
+                                    statusCallback),
+                    statusCallback);
+        }
+
+        @Override
+        public void unregisterDataRequestObserver(
+                int dataType,
+                PendingIntent dataRequestPendingIntent,
+                RemoteCallback statusCallback) {
+            Slog.i(TAG, "WearableSensingManagerInternal unregisterDataRequestObserver.");
+            Objects.requireNonNull(dataRequestPendingIntent);
+            Objects.requireNonNull(statusCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available.");
+                WearableSensingManagerPerUserService.notifyStatusCallback(
+                        statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+                return;
+            }
+            int userId = UserHandle.getCallingUserId();
+            int previousDataRequestObserverId;
+            String pendingIntentCreatorPackage;
+            synchronized (mDataRequestObserverContexts) {
+                DataRequestObserverContext previousObserverContext =
+                        getDataRequestObserverContext(dataType, userId, dataRequestPendingIntent);
+                if (previousObserverContext == null) {
+                    Slog.w(TAG, "Previous observer not found, cannot unregister.");
+                    return;
+                }
+                mDataRequestObserverContexts.remove(previousObserverContext);
+                previousDataRequestObserverId = previousObserverContext.mDataRequestObserverId;
+                pendingIntentCreatorPackage =
+                        previousObserverContext.mDataRequestPendingIntent.getCreatorPackage();
+            }
+            callPerUserServiceIfExist(
+                    service ->
+                            service.onUnregisterDataRequestObserver(
+                                    dataType,
+                                    previousDataRequestObserverId,
+                                    pendingIntentCreatorPackage,
+                                    statusCallback),
+                    statusCallback);
+        }
+
+        @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
             new WearableSensingShellCommand(WearableSensingManagerService.this).exec(
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 19ea9f9..ee865d3 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1610,7 +1610,7 @@
                 Slog.i(LOG_TAG, "computeChangedWindows()");
             }
 
-            final List<WindowInfo> windows = new ArrayList<>();
+            final List<WindowInfo> windows;
             final List<AccessibilityWindow> visibleWindows = new ArrayList<>();
             final int topFocusedDisplayId;
             IBinder topFocusedWindowToken = null;
@@ -1640,69 +1640,11 @@
                 }
                 final Display display = dc.getDisplay();
                 display.getRealSize(mTempPoint);
-                final int screenWidth = mTempPoint.x;
-                final int screenHeight = mTempPoint.y;
-
-                Region unaccountedSpace = mTempRegion;
-                unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
                 mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
                         mDisplayId, visibleWindows);
-                Set<IBinder> addedWindows = mTempBinderSet;
-                addedWindows.clear();
 
-                boolean focusedWindowAdded = false;
-
-                final int visibleWindowCount = visibleWindows.size();
-
-                // Iterate until we figure out what is touchable for the entire screen.
-                for (int i = 0; i < visibleWindowCount; i++) {
-                    final AccessibilityWindow a11yWindow = visibleWindows.get(i);
-                    final Region regionInWindow = new Region();
-                    a11yWindow.getTouchableRegionInWindow(regionInWindow);
-                    if (windowMattersToAccessibility(a11yWindow, regionInWindow,
-                            unaccountedSpace)) {
-                        addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
-                        if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
-                            updateUnaccountedSpace(a11yWindow, unaccountedSpace);
-                        }
-                        focusedWindowAdded |= a11yWindow.isFocused();
-                    } else if (a11yWindow.isUntouchableNavigationBar()) {
-                        // If this widow is navigation bar without touchable region, accounting the
-                        // region of navigation bar inset because all touch events from this region
-                        // would be received by launcher, i.e. this region is a un-touchable one
-                        // for the application.
-                        unaccountedSpace.op(
-                                getSystemBarInsetsFrame(
-                                        mService.mWindowMap.get(a11yWindow.getWindowInfo().token)),
-                                unaccountedSpace,
-                                Region.Op.REVERSE_DIFFERENCE);
-                    }
-
-                    if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
-                        break;
-                    }
-                }
-
-                // Remove child/parent references to windows that were not added.
-                final int windowCount = windows.size();
-                for (int i = 0; i < windowCount; i++) {
-                    WindowInfo window = windows.get(i);
-                    if (!addedWindows.contains(window.parentToken)) {
-                        window.parentToken = null;
-                    }
-                    if (window.childTokens != null) {
-                        final int childTokenCount = window.childTokens.size();
-                        for (int j = childTokenCount - 1; j >= 0; j--) {
-                            if (!addedWindows.contains(window.childTokens.get(j))) {
-                                window.childTokens.remove(j);
-                            }
-                        }
-                        // Leave the child token list if empty.
-                    }
-                }
-
-                addedWindows.clear();
+                windows = buildWindowInfoListLocked(visibleWindows, mTempPoint);
 
                 // Gets the top focused display Id and window token for supporting multi-display.
                 topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
@@ -1718,6 +1660,74 @@
             mInitialized = true;
         }
 
+        /**
+         * From a list of windows, decides windows to be exposed to accessibility based on touchable
+         * region in the screen.
+         */
+        private List<WindowInfo> buildWindowInfoListLocked(List<AccessibilityWindow> visibleWindows,
+                Point screenSize) {
+            final List<WindowInfo> windows = new ArrayList<>();
+            final Set<IBinder> addedWindows = mTempBinderSet;
+            addedWindows.clear();
+
+            boolean focusedWindowAdded = false;
+
+            final int visibleWindowCount = visibleWindows.size();
+
+            Region unaccountedSpace = mTempRegion;
+            unaccountedSpace.set(0, 0, screenSize.x, screenSize.y);
+
+            // Iterate until we figure out what is touchable for the entire screen.
+            for (int i = 0; i < visibleWindowCount; i++) {
+                final AccessibilityWindow a11yWindow = visibleWindows.get(i);
+                final Region regionInWindow = new Region();
+                a11yWindow.getTouchableRegionInWindow(regionInWindow);
+                if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {
+                    addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
+                    if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
+                        updateUnaccountedSpace(a11yWindow, unaccountedSpace);
+                    }
+                    focusedWindowAdded |= a11yWindow.isFocused();
+                } else if (a11yWindow.isUntouchableNavigationBar()) {
+                    // If this widow is navigation bar without touchable region, accounting the
+                    // region of navigation bar inset because all touch events from this region
+                    // would be received by launcher, i.e. this region is a un-touchable one
+                    // for the application.
+                    unaccountedSpace.op(
+                            getSystemBarInsetsFrame(
+                                    mService.mWindowMap.get(a11yWindow.getWindowInfo().token)),
+                            unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+                }
+
+                if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+                    break;
+                }
+            }
+
+            // Remove child/parent references to windows that were not added.
+            final int windowCount = windows.size();
+            for (int i = 0; i < windowCount; i++) {
+                WindowInfo window = windows.get(i);
+                if (!addedWindows.contains(window.parentToken)) {
+                    window.parentToken = null;
+                }
+                if (window.childTokens != null) {
+                    final int childTokenCount = window.childTokens.size();
+                    for (int j = childTokenCount - 1; j >= 0; j--) {
+                        if (!addedWindows.contains(window.childTokens.get(j))) {
+                            window.childTokens.remove(j);
+                        }
+                    }
+                    // Leave the child token list if empty.
+                }
+            }
+
+            addedWindows.clear();
+
+            return windows;
+        }
+
         // Some windows should be excluded from unaccounted space computation, though they still
         // should be reported
         private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 85580ac..d99000e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -591,9 +591,18 @@
 
             // Carefully collect grants without holding lock
             if (activityInfo != null) {
-                intentGrants = supervisor.mService.mUgmInternal.checkGrantUriPermissionFromIntent(
-                        intent, resolvedCallingUid, activityInfo.applicationInfo.packageName,
-                        UserHandle.getUserId(activityInfo.applicationInfo.uid));
+                if (android.security.Flags.contentUriPermissionApis()) {
+                    intentGrants = supervisor.mService.mUgmInternal
+                            .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid,
+                                    activityInfo.applicationInfo.packageName,
+                                    UserHandle.getUserId(activityInfo.applicationInfo.uid),
+                                    activityInfo.requireContentUriPermissionFromCaller);
+                } else {
+                    intentGrants = supervisor.mService.mUgmInternal
+                            .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid,
+                                    activityInfo.applicationInfo.packageName,
+                                    UserHandle.getUserId(activityInfo.applicationInfo.uid));
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0def5a1..8773366 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -276,6 +276,7 @@
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
 import com.android.server.firewall.IntentFirewall;
+import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
 import com.android.server.pm.UserManagerService;
 import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
@@ -317,7 +318,6 @@
  * {@hide}
  */
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
-    private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
     static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
     static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
@@ -381,6 +381,7 @@
     private PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
+    GrammaticalInflectionManagerInternal mGrammaticalManagerInternal;
     PendingIntentController mPendingIntentController;
     IntentFirewall mIntentFirewall;
 
@@ -881,6 +882,8 @@
             mActivityClientController.onSystemReady();
             // TODO(b/258792202) Cleanup once ASM is ready to launch
             ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm);
+            mGrammaticalManagerInternal = LocalServices.getService(
+                    GrammaticalInflectionManagerInternal.class);
         }
     }
 
@@ -938,13 +941,8 @@
             configuration.setLayoutDirection(configuration.locale);
         }
 
-        // Retrieve the grammatical gender from system property, set it into configuration which
-        // will get updated later if the grammatical gender raw value of current configuration is
-        // {@link Configuration#GRAMMATICAL_GENDER_UNDEFINED}.
-        if (configuration.getGrammaticalGenderRaw() == Configuration.GRAMMATICAL_GENDER_UNDEFINED) {
-            configuration.setGrammaticalGender(SystemProperties.getInt(GRAMMATICAL_GENDER_PROPERTY,
-                    Configuration.GRAMMATICAL_GENDER_UNDEFINED));
-        }
+        configuration.setGrammaticalGender(
+                mGrammaticalManagerInternal.retrieveSystemGrammaticalGender(configuration));
 
         synchronized (mGlobalLock) {
             mForceResizableActivities = forceResizable;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 25646f1..609ad1e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -83,6 +83,7 @@
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
+import static com.android.systemui.shared.Flags.enableHomeDelay;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -1444,6 +1445,7 @@
             aInfo = info.first;
             homeIntent = info.second;
         }
+
         if (aInfo == null || homeIntent == null) {
             return false;
         }
@@ -1452,6 +1454,11 @@
             return false;
         }
 
+        if (enableHomeDelay() && !mService.mAmInternal.getThemeOverlayReadiness()) {
+            Slog.d(TAG, "ThemeHomeDelay: Home launch was deferred.");
+            return false;
+        }
+
         // Updates the home component of the intent.
         homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
         homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6d2e8cc..6acf1f3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -28,6 +28,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.am.ProcessList.INVALID_ADJ;
+import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
 import static com.android.server.wm.ActivityRecord.State.DESTROYING;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -298,6 +299,8 @@
      */
     private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
 
+    private boolean mCanUseSystemGrammaticalGender;
+
     public WindowProcessController(@NonNull ActivityTaskManagerService atm,
             @NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
             @NonNull WindowProcessListener listener) {
@@ -319,6 +322,9 @@
             mIsActivityConfigOverrideAllowed = false;
         }
 
+        mCanUseSystemGrammaticalGender = mAtm.mGrammaticalManagerInternal != null
+                && mAtm.mGrammaticalManagerInternal.canGetSystemGrammaticalGender(mUid,
+                mInfo.packageName);
         onConfigurationChanged(atm.getGlobalConfiguration());
         mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
     }
@@ -1568,6 +1574,11 @@
             return;
         }
 
+        if (mCanUseSystemGrammaticalGender) {
+            config.setGrammaticalGender(
+                    mAtm.mGrammaticalManagerInternal.getSystemGrammaticalGender(mUserId));
+        }
+
         if (mPauseConfigurationDispatchCount > 0) {
             mHasPendingConfigurationChange = true;
             return;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 810090a..cbbcd96 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -238,7 +238,7 @@
     // to the underlying bitmap, so even if the java object is released, we will still have access
     // to it.
     return SpriteIcon(pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX,
-                      pointerIcon.hotSpotY);
+                      pointerIcon.hotSpotY, pointerIcon.drawNativeDropShadow);
 }
 
 enum {
@@ -1760,13 +1760,14 @@
             animationData.durationPerFrame =
                     milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
             animationData.animationFrames.reserve(numFrames);
-            animationData.animationFrames.push_back(SpriteIcon(
-                    pointerIcon.bitmap, pointerIcon.style,
-                    pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+            animationData.animationFrames.emplace_back(pointerIcon.bitmap, pointerIcon.style,
+                                                       pointerIcon.hotSpotX, pointerIcon.hotSpotY,
+                                                       pointerIcon.drawNativeDropShadow);
             for (size_t i = 0; i < numFrames - 1; ++i) {
-              animationData.animationFrames.push_back(SpriteIcon(
-                      pointerIcon.bitmapFrames[i], pointerIcon.style,
-                      pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                animationData.animationFrames.emplace_back(pointerIcon.bitmapFrames[i],
+                                                           pointerIcon.style, pointerIcon.hotSpotX,
+                                                           pointerIcon.hotSpotY,
+                                                           pointerIcon.drawNativeDropShadow);
             }
         }
     }
@@ -2610,7 +2611,7 @@
         icon = std::make_unique<SpriteIcon>(pointerIcon.bitmap.copy(
                                                     ANDROID_BITMAP_FORMAT_RGBA_8888),
                                             pointerIcon.style, pointerIcon.hotSpotX,
-                                            pointerIcon.hotSpotY);
+                                            pointerIcon.hotSpotY, pointerIcon.drawNativeDropShadow);
     } else {
         icon = pointerIcon.style;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6f0985a..f87fd8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -6016,10 +6016,10 @@
             // Make sure the caller has any active admin with the right policy or
             // the required permission.
             if (isUnicornFlagEnabled()) {
-                admin = enforcePermissionAndGetEnforcingAdmin(
+                admin = enforcePermissionsAndGetEnforcingAdmin(
                         /* admin= */ null,
-                        /* permission= */ MANAGE_DEVICE_POLICY_LOCK,
-                        USES_POLICY_FORCE_LOCK,
+                        /* permissions= */ new String[]{MANAGE_DEVICE_POLICY_LOCK, LOCK_DEVICE},
+                        /* deviceAdminPolicy= */ USES_POLICY_FORCE_LOCK,
                         caller.getPackageName(),
                         getAffectedUser(parent)
                  ).getActiveAdmin();
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
index d479e52..682ed91 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -32,6 +32,7 @@
         ":BackgroundInstallControlServiceTestApp",
         ":BackgroundInstallControlMockApp1",
         ":BackgroundInstallControlMockApp2",
+        ":BackgroundInstallControlMockApp3",
     ],
     test_suites: [
         "general-tests",
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
index 1e7a78a..a352851 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
+++ b/services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml
@@ -34,6 +34,9 @@
         <option name="push-file"
                 key="BackgroundInstallControlMockApp2.apk"
                 value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" />
+        <option name="push-file"
+            key="BackgroundInstallControlMockApp3.apk"
+            value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
index c99e712..5092a46 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
+++ b/services/tests/BackgroundInstallControlServiceTests/host/src/com/android/server/pm/test/BackgroundInstallControlServiceHostTest.java
@@ -68,6 +68,20 @@
         assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNull();
     }
 
+    @Test
+    public void testRegisterCallback() throws Exception {
+        runDeviceTest(
+                "BackgroundInstallControlServiceTest",
+                "testRegisterBackgroundInstallControlCallback");
+    }
+
+    @Test
+    public void testUnregisterCallback() throws Exception {
+        runDeviceTest(
+                "BackgroundInstallControlServiceTest",
+                "testUnregisterBackgroundInstallControlCallback");
+    }
+
     private void installPackage(String path) throws DeviceNotAvailableException {
         String cmd = "pm install -t --force-queryable " + path;
         CommandResult result = getDevice().executeShellV2Command(cmd);
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
index b23f591..ac041f4 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/BackgroundInstallControlServiceTestApp/src/com/android/server/pm/test/app/BackgroundInstallControlServiceTest.java
@@ -16,38 +16,59 @@
 
 package com.android.server.pm.test.app;
 
-import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES;
-
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.IBackgroundInstallControlService;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
+import android.os.IRemoteCallback;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.Pair;
 
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
 public class BackgroundInstallControlServiceTest {
     private static final String TAG = "BackgroundInstallControlServiceTest";
+    private static final String ACTION_INSTALL_COMMIT =
+            "com.android.server.pm.test.app.BackgroundInstallControlServiceTest"
+                    + ".ACTION_INSTALL_COMMIT";
     private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3";
 
+    private static final String TEST_DATA_DIR = "/data/local/tmp/";
+
+    private static final String MOCK_APK_FILE = "BackgroundInstallControlMockApp3.apk";
     private IBackgroundInstallControlService mIBics;
 
     @Before
@@ -74,10 +95,9 @@
                                         PackageManager.MATCH_ALL, Process.myUserHandle()
                                                 .getIdentifier());
                             } catch (RemoteException e) {
-                                throw new RuntimeException(e);
+                                throw e.rethrowFromSystemServer();
                             }
-                        },
-                        GET_BACKGROUND_INSTALLED_PACKAGES);
+                        });
         assertThat(slice).isNotNull();
 
         var packageList = slice.getList();
@@ -94,4 +114,150 @@
                         .collect(Collectors.toSet());
         assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames);
     }
+
+    @Test
+    public void testRegisterBackgroundInstallControlCallback()
+            throws Exception {
+        String testPackageName = "test";
+        int testUserId = 1;
+        ArrayList<Pair<String, Integer>> sharedResource = new ArrayList<>();
+        IRemoteCallback testCallback =
+                new IRemoteCallback.Stub() {
+                    private final ArrayList<Pair<String, Integer>> mArray = sharedResource;
+
+                    @Override
+                    public void sendResult(Bundle data) throws RemoteException {
+                        mArray.add(new Pair(testPackageName, testUserId));
+                    }
+                };
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mIBics,
+                (bics) -> {
+                    try {
+                        bics.registerBackgroundInstallCallback(testCallback);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                });
+        installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME);
+
+        assertUntil(() -> sharedResource.size() == 1, 2000);
+        assertThat(sharedResource.get(0).first).isEqualTo(testPackageName);
+        assertThat(sharedResource.get(0).second).isEqualTo(testUserId);
+    }
+
+    @Test
+    public void testUnregisterBackgroundInstallControlCallback() {
+        String testValue = "test";
+        ArrayList<String> sharedResource = new ArrayList<>();
+        IRemoteCallback testCallback =
+                new IRemoteCallback.Stub() {
+                    private final ArrayList<String> mArray = sharedResource;
+
+                    @Override
+                    public void sendResult(Bundle data) throws RemoteException {
+                        mArray.add(testValue);
+                    }
+                };
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mIBics,
+                (bics) -> {
+                    try {
+                        bics.registerBackgroundInstallCallback(testCallback);
+                        bics.unregisterBackgroundInstallCallback(testCallback);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                });
+        installPackage(TEST_DATA_DIR + MOCK_APK_FILE, MOCK_PACKAGE_NAME);
+
+        assertUntil(sharedResource::isEmpty, 2000);
+    }
+
+    private static boolean installPackage(String apkPath, String packageName) {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final CountDownLatch installLatch = new CountDownLatch(1);
+        final BroadcastReceiver installReceiver =
+                new BroadcastReceiver() {
+                    public void onReceive(Context context, Intent intent) {
+                        int packageInstallStatus =
+                                intent.getIntExtra(
+                                        PackageInstaller.EXTRA_STATUS,
+                                        PackageInstaller.STATUS_FAILURE_INVALID);
+                        if (packageInstallStatus == PackageInstaller.STATUS_SUCCESS) {
+                            installLatch.countDown();
+                        }
+                    }
+                };
+        final IntentFilter intentFilter = new IntentFilter(ACTION_INSTALL_COMMIT);
+        context.registerReceiver(installReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+        PackageInstaller.SessionParams params =
+                new PackageInstaller.SessionParams(
+                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED);
+        try {
+            int sessionId = packageInstaller.createSession(params);
+            PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+            OutputStream out = session.openWrite(packageName, 0, -1);
+            FileInputStream fis = new FileInputStream(apkPath);
+            byte[] buffer = new byte[65536];
+            int size;
+            while ((size = fis.read(buffer)) != -1) {
+                out.write(buffer, 0, size);
+            }
+            session.fsync(out);
+            fis.close();
+            out.close();
+
+            runWithShellPermissionIdentity(
+                    () -> {
+                        session.commit(createPendingIntent(context).getIntentSender());
+                        installLatch.await(5, TimeUnit.SECONDS);
+                    });
+            return true;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static PendingIntent createPendingIntent(Context context) {
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(
+                        context,
+                        1,
+                        new Intent(ACTION_INSTALL_COMMIT)
+                                .setPackage(
+                                        BackgroundInstallControlServiceTest.class.getPackageName()),
+                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
+        return pendingIntent;
+    }
+
+    private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
+            throws Exception {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+        try {
+            command.run();
+        } finally {
+            InstrumentationRegistry.getInstrumentation()
+                    .getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+    }
+
+    private static void assertUntil(Supplier<Boolean> condition, int timeoutMs) {
+        long endTime = System.currentTimeMillis() + timeoutMs;
+        while (System.currentTimeMillis() <= endTime) {
+            if (condition.get()) return;
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        assertThat(condition.get()).isTrue();
+    }
 }
\ No newline at end of file
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
index 7804f4c..39b0ff7 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/test-app/MockApp/Android.bp
@@ -50,3 +50,11 @@
         "--rename-manifest-package com.android.servicestests.apps.bicmockapp2",
     ],
 }
+
+android_test_helper_app {
+    name: "BackgroundInstallControlMockApp3",
+    defaults: ["bic-mock-app-defaults"],
+    aaptflags: [
+        "--rename-manifest-package com.android.servicestests.apps.bicmockapp3",
+    ],
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 2c8b1cd..349b831 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -54,7 +54,8 @@
         ParsedActivity::getTheme,
         ParsedActivity::getUiOptions,
         ParsedActivity::isSupportsSizeChanges,
-        ParsedActivity::getRequiredDisplayCategory
+        ParsedActivity::getRequiredDisplayCategory,
+        ParsedActivity::getRequireContentUriPermissionFromCaller
     )
 
     override fun mainComponentSubclassExtraParams() = listOf(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
new file mode 100644
index 0000000..574f369
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY;
+import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+
+/** Unit tests for {@link BackgroundInstallControlCallbackHelper} */
+@Presubmit
+@RunWith(JUnit4.class)
+public class BackgroundInstallControlCallbackHelperTest {
+
+    private final IRemoteCallback mCallback =
+            spy(
+                    new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle extras) {}
+                    });
+
+    private BackgroundInstallControlCallbackHelper mCallbackHelper;
+
+    @Before
+    public void setup() {
+        mCallbackHelper = new BackgroundInstallControlCallbackHelper();
+    }
+
+    @Test
+    public void registerBackgroundInstallControlCallback_registers_successfully() {
+        mCallbackHelper.registerBackgroundInstallCallback(mCallback);
+
+        synchronized (mCallbackHelper.mCallbacks) {
+            assertEquals(1, mCallbackHelper.mCallbacks.getRegisteredCallbackCount());
+            assertEquals(mCallback, mCallbackHelper.mCallbacks.getRegisteredCallbackItem(0));
+        }
+    }
+
+    @Test
+    public void unregisterBackgroundInstallControlCallback_unregisters_successfully() {
+        synchronized (mCallbackHelper.mCallbacks) {
+            mCallbackHelper.mCallbacks.register(mCallback);
+        }
+
+        mCallbackHelper.unregisterBackgroundInstallCallback(mCallback);
+
+        synchronized (mCallbackHelper.mCallbacks) {
+            assertEquals(0, mCallbackHelper.mCallbacks.getRegisteredCallbackCount());
+        }
+    }
+
+    @Test
+    public void notifyAllCallbacks_broadcastsToCallbacks()
+            throws RemoteException {
+        String testPackageName = "testname";
+        int testUserId = 1;
+        mCallbackHelper.registerBackgroundInstallCallback(mCallback);
+
+        mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName);
+
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture());
+        Bundle receivedBundle = bundleCaptor.getValue();
+        assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY));
+        assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY));
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ad6e2c6..3743483 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -154,6 +154,7 @@
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "services.core",
+        "flag-junit",
     ],
     srcs: [
         "src/com/android/server/uri/**/*.java",
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 9cdaec6..7a77392 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -472,6 +472,7 @@
 
         verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
         verify(mSideFpsController).hide(anyInt());
+        verify(mHal, times(2)).setIgnoreDisplayTouches(false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 951c9393..3ee54f5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -327,6 +328,7 @@
 
         verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt());
         verify(mSideFpsController).hide(anyInt());
+        verify(mHal, times(2)).setIgnoreDisplayTouches(false);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 99fa30c..1d3dacc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -169,14 +169,14 @@
                         .setEarcSupported(true)
                         .build();
         mHdmiPortInfo[3] =
-                new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_INPUT, 0x3000)
+                new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000)
                         .setCecSupported(true)
                         .setMhlSupported(false)
                         .setArcSupported(false)
                         .setEarcSupported(false)
                         .build();
         mHdmiPortInfo[4] =
-                new HdmiPortInfo.Builder(4, HdmiPortInfo.PORT_OUTPUT, 0x3000)
+                new HdmiPortInfo.Builder(5, HdmiPortInfo.PORT_OUTPUT, 0x3000)
                         .setCecSupported(true)
                         .setMhlSupported(false)
                         .setArcSupported(false)
@@ -841,6 +841,65 @@
     }
 
     @Test
+    public void onHotPlugIn_CecDisabledOnTv_CecNotAvailable() {
+        HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+        mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+        mTestLooper.dispatchAll();
+
+        mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+        mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        mHdmiControlServiceSpy.onHotplug(4, true);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveDevicePowerStatus =
+                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+                        mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+                        Constants.ADDR_TV);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        // Wait for DevicePowerStatusAction to finish.
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+        mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+        assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse();
+    }
+
+    @Test
+    public void onHotPlugIn_CecEnabledOnTv_CecAvailable() {
+        HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+        mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+        mTestLooper.dispatchAll();
+
+        mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+        mHdmiControlServiceSpy.playback().removeAction(DevicePowerStatusAction.class);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        mHdmiControlServiceSpy.onHotplug(4, true);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage giveDevicePowerStatus =
+                HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+                        mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+                        Constants.ADDR_TV);
+        HdmiCecMessage reportPowerStatus =
+                HdmiCecMessageBuilder.buildReportPowerStatus(
+                        Constants.ADDR_TV,
+                        mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+                        HdmiControlManager.POWER_STATUS_ON);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        mNativeWrapper.onCecMessage(reportPowerStatus);
+        mTestLooper.dispatchAll();
+
+        assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+        assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue();
+    }
+    @Test
     public void handleCecCommand_errorParameter_returnsAbortInvalidOperand() {
         // Validity ERROR_PARAMETER. Taken from HdmiCecMessageValidatorTest#isValid_menuStatus
         HdmiCecMessage message = HdmiUtils.buildMessage("80:8D:03");
diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index 8656f60..bf87e3a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -111,6 +111,8 @@
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
     @Mock
     private PermissionManagerServiceInternal mPermissionManager;
+    @Mock
+    private BackgroundInstallControlCallbackHelper mCallbackHelper;
 
     @Captor
     private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;
@@ -982,5 +984,11 @@
         public File getDiskFile() {
             return mFile;
         }
+
+
+        @Override
+        public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() {
+            return mCallbackHelper;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 3218586..24abc18 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.uri;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
+
 import static com.android.server.uri.UriGrantsMockContext.FLAG_PERSISTABLE;
 import static com.android.server.uri.UriGrantsMockContext.FLAG_PREFIX;
 import static com.android.server.uri.UriGrantsMockContext.FLAG_READ;
@@ -57,22 +59,49 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArraySet;
 
-import androidx.test.InstrumentationRegistry;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
 
+@RunWith(Parameterized.class)
 public class UriGrantsManagerServiceTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
 
+    /**
+     * Why this class needs to test all combinations of
+     * {@link android.security.Flags#FLAG_CONTENT_URI_PERMISSION_APIS}:
+     *
+     * <p>Although tests in this class don't directly query the flag, its value
+     * is needed for {@link UriGrantsManagerInternal#checkGrantUriPermissionFromIntent}. This is
+     * particularly important for host side tests (Ravenwood), which cannot read flag values from
+     * the device and must have them set explicitly.
+     */
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getFlags() {
+        return FlagsParameterization.allCombinationsOf(
+                android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS);
+    }
+
+    public UriGrantsManagerServiceTest(FlagsParameterization flags) {
+        mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT, flags);
+    }
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule;
+
     private UriGrantsMockContext mContext;
     private UriGrantsManagerInternal mService;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 90493d4..295b124 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -346,6 +346,7 @@
         doReturn(true).when(amInternal).hasStartedUserState(anyInt());
         doReturn(false).when(amInternal).shouldConfirmCredentials(anyInt());
         doReturn(false).when(amInternal).isActivityStartsLoggingEnabled();
+        doReturn(true).when(amInternal).getThemeOverlayReadiness();
         LocalServices.addService(ActivityManagerInternal.class, amInternal);
 
         final ActivityManagerService amService =
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 331caa1..7ad26c9 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,10 @@
 
 package android.telecom;
 
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.media.ToneGenerator;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,6 +28,8 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.text.TextUtils;
 
+import com.android.server.telecom.flags.Flags;
+
 import java.util.Objects;
 
 /**
@@ -169,7 +174,9 @@
     }
 
     /**
-     * Creates a new DisconnectCause instance.
+     * Creates a new DisconnectCause instance. This is used by Telephony to pass in extra debug
+     * info to Telecom regarding the disconnect cause.
+     *
      * @param code The code for the disconnect cause.
      * @param label The localized label to show to the user to explain the disconnect.
      * @param description The localized description to show to the user to explain the disconnect.
@@ -180,7 +187,10 @@
      * @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
      * @hide
      */
-    public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    public DisconnectCause(int code, @NonNull CharSequence label,
+            @NonNull CharSequence description, @NonNull String reason,
             int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
             @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
             @Nullable ImsReasonInfo imsReasonInfo) {
@@ -241,28 +251,40 @@
     }
 
     /**
-     * Returns the telephony {@link android.telephony.DisconnectCause} for the call.
+     * Returns the telephony {@link android.telephony.DisconnectCause} for the call. This is only
+     * used internally by Telecom for providing extra debug information from Telephony.
+     *
      * @return The disconnect cause.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public @Annotation.DisconnectCauses int getTelephonyDisconnectCause() {
         return mTelephonyDisconnectCause;
     }
 
     /**
-     * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call.
+     * Returns the telephony {@link android.telephony.PreciseDisconnectCause} for the call. This is
+     * only used internally by Telecom for providing extra debug information from Telephony.
+     *
      * @return The precise disconnect cause.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public @Annotation.PreciseDisconnectCauses int getTelephonyPreciseDisconnectCause() {
         return mTelephonyPreciseDisconnectCause;
     }
 
     /**
-     * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection.
+     * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This is
+     * only used internally by Telecom for providing extra debug information from Telephony.
+     *
      * @return The {@link ImsReasonInfo} or {@code null} if not known.
      * @hide
      */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
     public @Nullable ImsReasonInfo getImsReasonInfo() {
         return mImsReasonInfo;
     }
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index b59e855..5af2c34 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,6 +39,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.telephony.flags.Flags;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.annotation.Retention;
@@ -759,6 +762,20 @@
     public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
 
     /**
+     * Return the available memory in bytes of the eUICC.
+     *
+     * @param slotId ID of the SIM slot being queried.
+     * @return the available memory in bytes.
+     * @see android.telephony.euicc.EuiccManager#getAvailableMemoryInBytes
+     */
+    @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+    public long onGetAvailableMemoryInBytes(int slotId) {
+        // stub implementation, LPA needs to implement this
+        throw new UnsupportedOperationException("The connected LPA does not implement"
+                + "EuiccService#onGetAvailableMemoryInBytes(int)");
+    }
+
+    /**
      * Dump to a provided printWriter.
      */
     public void dump(@NonNull PrintWriter printWriter) {
@@ -834,6 +851,22 @@
         }
 
         @Override
+        @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+        public void getAvailableMemoryInBytes(
+                int slotId, IGetAvailableMemoryInBytesCallback callback) {
+            mExecutor.execute(
+                    () -> {
+                        long availableMemoryInBytes =
+                                EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+                        try {
+                            callback.onSuccess(availableMemoryInBytes);
+                        } catch (RemoteException e) {
+                            // Can't communicate with the phone process; ignore.
+                        }
+                    });
+        }
+
+        @Override
         public void startOtaIfNecessary(
                 int slotId, IOtaStatusChangedCallback statusChangedCallback) {
             mExecutor.execute(new Runnable() {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index f8d5ae9..0f8c72b 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -19,6 +19,7 @@
 import android.service.euicc.IDeleteSubscriptionCallback;
 import android.service.euicc.IDownloadSubscriptionCallback;
 import android.service.euicc.IEraseSubscriptionsCallback;
+import android.service.euicc.IGetAvailableMemoryInBytesCallback;
 import android.service.euicc.IGetDefaultDownloadableSubscriptionListCallback;
 import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
 import android.service.euicc.IGetEidCallback;
@@ -60,4 +61,5 @@
     void retainSubscriptionsForFactoryReset(
             int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
     void dump(in IEuiccServiceDumpResultCallback callback);
-}
\ No newline at end of file
+    void getAvailableMemoryInBytes(int slotId, in IGetAvailableMemoryInBytesCallback callback);
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
copy to telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
index 128f58b..bd6d19b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityLaunchAnimatorKosmos.kt
+++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.animation
+package android.service.euicc;
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.activityLaunchAnimator by Kosmos.Fixture { ActivityLaunchAnimator() }
+/** @hide */
+oneway interface IGetAvailableMemoryInBytesCallback {
+    void onSuccess(long availableMemoryInBytes);
+}
diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java
index 0f54e8d..3c11da5 100644
--- a/telephony/java/android/telephony/DomainSelectionService.java
+++ b/telephony/java/android/telephony/DomainSelectionService.java
@@ -90,7 +90,7 @@
  */
 @SystemApi
 @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
-public class DomainSelectionService extends Service {
+public abstract class DomainSelectionService extends Service {
 
     private static final String LOG_TAG = "DomainSelectionService";
 
@@ -152,7 +152,7 @@
         private boolean mIsExitedFromAirplaneMode;
         private @Nullable ImsReasonInfo mImsReasonInfo;
         private @PreciseDisconnectCauses int mCause;
-        private @Nullable EmergencyRegResult mEmergencyRegResult;
+        private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
 
         /**
          * @param slotIndex The logical slot index.
@@ -172,7 +172,7 @@
                 @Nullable Uri address, @SelectorType int selectorType,
                 boolean video, boolean emergency, boolean isTest, boolean exited,
                 @Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause,
-                @Nullable EmergencyRegResult regResult) {
+                @Nullable EmergencyRegistrationResult regResult) {
             mSlotIndex = slotIndex;
             mSubId = subscriptionId;
             mCallId = callId;
@@ -184,7 +184,7 @@
             mIsExitedFromAirplaneMode = exited;
             mImsReasonInfo = imsReasonInfo;
             mCause = cause;
-            mEmergencyRegResult = regResult;
+            mEmergencyRegistrationResult = regResult;
         }
 
         /**
@@ -204,7 +204,7 @@
             mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode;
             mImsReasonInfo = s.mImsReasonInfo;
             mCause = s.mCause;
-            mEmergencyRegResult = s.mEmergencyRegResult;
+            mEmergencyRegistrationResult = s.mEmergencyRegistrationResult;
         }
 
         /**
@@ -296,8 +296,8 @@
         /**
          * @return The current registration state of cellular network.
          */
-        public @Nullable EmergencyRegResult getEmergencyRegResult() {
-            return mEmergencyRegResult;
+        public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() {
+            return mEmergencyRegistrationResult;
         }
 
         @Override
@@ -313,7 +313,7 @@
                     + ", airplaneMode=" + mIsExitedFromAirplaneMode
                     + ", reasonInfo=" + mImsReasonInfo
                     + ", cause=" + mCause
-                    + ", regResult=" + mEmergencyRegResult
+                    + ", regResult=" + mEmergencyRegistrationResult
                     + " }";
         }
 
@@ -331,14 +331,15 @@
                     && mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode
                     && equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo)
                     && mCause == that.mCause
-                    && equalsHandlesNulls(mEmergencyRegResult, that.mEmergencyRegResult);
+                    && equalsHandlesNulls(mEmergencyRegistrationResult,
+                            that.mEmergencyRegistrationResult);
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(mCallId, mAddress, mImsReasonInfo,
                     mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode,
-                    mEmergencyRegResult, mSlotIndex, mSubId, mSelectorType, mCause);
+                    mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause);
         }
 
         @Override
@@ -359,7 +360,7 @@
             out.writeBoolean(mIsExitedFromAirplaneMode);
             out.writeParcelable(mImsReasonInfo, 0);
             out.writeInt(mCause);
-            out.writeParcelable(mEmergencyRegResult, 0);
+            out.writeParcelable(mEmergencyRegistrationResult, 0);
         }
 
         private void readFromParcel(@NonNull Parcel in) {
@@ -376,8 +377,9 @@
             mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(),
                     android.telephony.ims.ImsReasonInfo.class);
             mCause = in.readInt();
-            mEmergencyRegResult = in.readParcelable(EmergencyRegResult.class.getClassLoader(),
-                    EmergencyRegResult.class);
+            mEmergencyRegistrationResult = in.readParcelable(
+                    EmergencyRegistrationResult.class.getClassLoader(),
+                    EmergencyRegistrationResult.class);
         }
 
         public static final @NonNull Creator<SelectionAttributes> CREATOR =
@@ -413,7 +415,7 @@
             private boolean mIsExitedFromAirplaneMode;
             private @Nullable ImsReasonInfo mImsReasonInfo;
             private @PreciseDisconnectCauses int mCause;
-            private @Nullable EmergencyRegResult mEmergencyRegResult;
+            private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
 
             /**
              * Default constructor for Builder.
@@ -430,7 +432,7 @@
              * @param callId The call identifier.
              * @return The same instance of the builder.
              */
-            public @NonNull Builder setCallId(@NonNull String callId) {
+            public @NonNull Builder setCallId(@Nullable String callId) {
                 mCallId = callId;
                 return this;
             }
@@ -441,7 +443,7 @@
              * @param address The dialed address.
              * @return The same instance of the builder.
              */
-            public @NonNull Builder setAddress(@NonNull Uri address) {
+            public @NonNull Builder setAddress(@Nullable Uri address) {
                 mAddress = address;
                 return this;
             }
@@ -497,7 +499,7 @@
              * @param info The reason why the last PS attempt failed.
              * @return The same instance of the builder.
              */
-            public @NonNull Builder setPsDisconnectCause(@NonNull ImsReasonInfo info) {
+            public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) {
                 mImsReasonInfo = info;
                 return this;
             }
@@ -519,8 +521,9 @@
              * @param regResult The current registration result for emergency services.
              * @return The same instance of the builder.
              */
-            public @NonNull Builder setEmergencyRegResult(@NonNull EmergencyRegResult regResult) {
-                mEmergencyRegResult = regResult;
+            public @NonNull Builder setEmergencyRegistrationResult(
+                    @Nullable EmergencyRegistrationResult regResult) {
+                mEmergencyRegistrationResult = regResult;
                 return this;
             }
 
@@ -532,7 +535,7 @@
                 return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress,
                         mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber,
                         mIsExitedFromAirplaneMode, mImsReasonInfo,
-                        mCause, mEmergencyRegResult);
+                        mCause, mEmergencyRegistrationResult);
             }
         }
     }
@@ -697,7 +700,7 @@
         public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
                 @EmergencyScanType int scanType, boolean resetScan,
                 @NonNull CancellationSignal signal,
-                @NonNull Consumer<EmergencyRegResult> consumer) {
+                @NonNull Consumer<EmergencyRegistrationResult> consumer) {
             try {
                 if (signal != null) signal.setOnCancelListener(this);
                 mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor);
@@ -721,17 +724,18 @@
 
         private class IWwanSelectorResultCallbackAdapter
                 extends IWwanSelectorResultCallback.Stub {
-            private final @NonNull Consumer<EmergencyRegResult> mConsumer;
+            private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer;
             private final @NonNull Executor mExecutor;
 
-            IWwanSelectorResultCallbackAdapter(@NonNull Consumer<EmergencyRegResult> consumer,
+            IWwanSelectorResultCallbackAdapter(
+                    @NonNull Consumer<EmergencyRegistrationResult> consumer,
                     @NonNull Executor executor) {
                 mConsumer = consumer;
                 mExecutor = executor;
             }
 
             @Override
-            public void onComplete(@NonNull EmergencyRegResult result) {
+            public void onComplete(@NonNull EmergencyRegistrationResult result) {
                 if (mConsumer == null) return;
 
                 executeMethodAsyncNoException(mExecutor,
@@ -759,9 +763,8 @@
      * @param attr Required to determine the domain.
      * @param callback The callback instance being registered.
      */
-    public void onDomainSelection(@NonNull SelectionAttributes attr,
-            @NonNull TransportSelectorCallback callback) {
-    }
+    public abstract void onDomainSelection(@NonNull SelectionAttributes attr,
+            @NonNull TransportSelectorCallback callback);
 
     /**
      * Notifies the change in {@link ServiceState} for a specific logical slot index.
@@ -836,7 +839,7 @@
 
     /** @hide */
     @Override
-    public @Nullable IBinder onBind(@Nullable Intent intent) {
+    public final @Nullable IBinder onBind(@Nullable Intent intent) {
         if (intent == null) return null;
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
             Log.i(LOG_TAG, "DomainSelectionService Bound.");
@@ -863,7 +866,7 @@
      * @return {@link Executor} instance.
      * @hide
      */
-    public @NonNull Executor getCachedExecutor() {
+    public final @NonNull Executor getCachedExecutor() {
         synchronized (mExecutorLock) {
             if (mExecutor == null) {
                 Executor e = onCreateExecutor();
diff --git a/telephony/java/android/telephony/EmergencyRegResult.aidl b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl
similarity index 93%
rename from telephony/java/android/telephony/EmergencyRegResult.aidl
rename to telephony/java/android/telephony/EmergencyRegistrationResult.aidl
index f722962..3056031 100644
--- a/telephony/java/android/telephony/EmergencyRegResult.aidl
+++ b/telephony/java/android/telephony/EmergencyRegistrationResult.aidl
@@ -16,4 +16,4 @@
 
 package android.telephony;
 
-parcelable EmergencyRegResult;
+parcelable EmergencyRegistrationResult;
diff --git a/telephony/java/android/telephony/EmergencyRegResult.java b/telephony/java/android/telephony/EmergencyRegistrationResult.java
similarity index 91%
rename from telephony/java/android/telephony/EmergencyRegResult.java
rename to telephony/java/android/telephony/EmergencyRegistrationResult.java
index 15579be..7041f5b 100644
--- a/telephony/java/android/telephony/EmergencyRegResult.java
+++ b/telephony/java/android/telephony/EmergencyRegistrationResult.java
@@ -35,7 +35,7 @@
  */
 @SystemApi
 @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
-public final class EmergencyRegResult implements Parcelable {
+public final class EmergencyRegistrationResult implements Parcelable {
 
     /**
      * Indicates the cellular network type of the acquired system.
@@ -101,7 +101,7 @@
      * @param iso The ISO-3166-1 alpha-2 country code equivalent, empty string if unknown.
      * @hide
      */
-    public EmergencyRegResult(
+    public EmergencyRegistrationResult(
             @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
             @NetworkRegistrationInfo.RegistrationState int regState,
             @NetworkRegistrationInfo.Domain int domain,
@@ -125,7 +125,7 @@
      * @param s Source emergency scan result
      * @hide
      */
-    public EmergencyRegResult(@NonNull EmergencyRegResult s) {
+    public EmergencyRegistrationResult(@NonNull EmergencyRegistrationResult s) {
         mAccessNetworkType = s.mAccessNetworkType;
         mRegState = s.mRegState;
         mDomain = s.mDomain;
@@ -139,9 +139,9 @@
     }
 
     /**
-     * Construct a EmergencyRegResult object from the given parcel.
+     * Construct a EmergencyRegistrationResult object from the given parcel.
      */
-    private EmergencyRegResult(@NonNull Parcel in) {
+    private EmergencyRegistrationResult(@NonNull Parcel in) {
         readFromParcel(in);
     }
 
@@ -258,7 +258,7 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        EmergencyRegResult that = (EmergencyRegResult) o;
+        EmergencyRegistrationResult that = (EmergencyRegistrationResult) o;
         return mAccessNetworkType == that.mAccessNetworkType
                 && mRegState == that.mRegState
                 && mDomain == that.mDomain
@@ -311,16 +311,16 @@
         mCountryIso = in.readString8();
     }
 
-    public static final @NonNull Creator<EmergencyRegResult> CREATOR =
-            new Creator<EmergencyRegResult>() {
-        @Override
-        public EmergencyRegResult createFromParcel(@NonNull Parcel in) {
-            return new EmergencyRegResult(in);
-        }
+    public static final @NonNull Creator<EmergencyRegistrationResult> CREATOR =
+            new Creator<EmergencyRegistrationResult>() {
+                @Override
+                public EmergencyRegistrationResult createFromParcel(@NonNull Parcel in) {
+                    return new EmergencyRegistrationResult(in);
+                }
 
-        @Override
-        public EmergencyRegResult[] newArray(int size) {
-            return new EmergencyRegResult[size];
-        }
-    };
+                @Override
+                public EmergencyRegistrationResult[] newArray(int size) {
+                    return new EmergencyRegistrationResult[size];
+                }
+            };
 }
diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java
index ea83815..b900af3 100644
--- a/telephony/java/android/telephony/WwanSelectorCallback.java
+++ b/telephony/java/android/telephony/WwanSelectorCallback.java
@@ -48,7 +48,8 @@
      */
     void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
             @EmergencyScanType int scanType, boolean resetScan,
-            @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegResult> consumer);
+            @NonNull CancellationSignal signal,
+            @NonNull Consumer<EmergencyRegistrationResult> consumer);
 
     /**
      * Notifies the FW that the domain has been selected. After this method is called,
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0fe43b3..7935d24 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.app.Activity;
 import android.app.PendingIntent;
@@ -863,6 +864,10 @@
      */
     public static final int ERROR_INVALID_PORT = 10017;
 
+    /** Temporary failure to retrieve available memory because eUICC is not ready. */
+    @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+    public static final long EUICC_MEMORY_FIELD_UNAVAILABLE = -1L;
+
     /**
      * Apps targeting on Android T and beyond will get exception whenever switchToSubscription
      * without portIndex is called for disable subscription.
@@ -963,6 +968,35 @@
     }
 
     /**
+     * Returns the available memory in bytes of the eUICC.
+     *
+     * @return the available memory in bytes. May be {@link #EUICC_MEMORY_FIELD_UNAVAILABLE} if the
+     *     eUICC is not ready. Check {@link #isEnabled} for more information.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC} or
+     *          device doesn't support querying this information from the eUICC.
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @FlaggedApi(Flags.FLAG_ESIM_AVAILABLE_MEMORY)
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                "carrier privileges"
+            })
+    public long getAvailableMemoryInBytes() {
+        if (!isEnabled()) {
+            return EUICC_MEMORY_FIELD_UNAVAILABLE;
+        }
+        try {
+            return getIEuiccController()
+                    .getAvailableMemoryInBytes(mCardId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the current status of eUICC OTA.
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
diff --git a/telephony/java/android/telephony/satellite/EnableRequestAttributes.java b/telephony/java/android/telephony/satellite/EnableRequestAttributes.java
new file mode 100644
index 0000000..bc9d230
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/EnableRequestAttributes.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * EnableRequestAttributes is used to store the attributes of the request
+ * {@link SatelliteManager#requestEnabled(EnableRequestAttributes, Executor, Consumer)}
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+public class EnableRequestAttributes {
+    /** {@code true} to enable satellite and {@code false} to disable satellite */
+    private boolean mIsEnabled;
+    /**
+     * {@code true} to enable demo mode and {@code false} to disable. When disabling satellite,
+     * {@code mIsDemoMode} is always considered as {@code false} by Telephony.
+     */
+    private boolean mIsDemoMode;
+    /**
+     * {@code true} means satellite is enabled for emergency mode, {@code false} otherwise. When
+     * disabling satellite, {@code isEmergencyMode} is always considered as {@code false} by
+     * Telephony.
+     */
+    private boolean mIsEmergencyMode;
+
+    /**
+     * Constructor from builder.
+     *
+     * @param builder Builder of {@link EnableRequestAttributes}.
+     */
+    private EnableRequestAttributes(@NonNull Builder builder) {
+        this.mIsEnabled = builder.mIsEnabled;
+        this.mIsDemoMode = builder.mIsDemoMode;
+        this.mIsEmergencyMode = builder.mIsEmergencyMode;
+    }
+
+    /**
+     * @return Whether satellite is to be enabled
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * @return Whether demo mode is to be enabled
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isDemoMode() {
+        return mIsDemoMode;
+    }
+
+    /**
+     * @return Whether satellite is to be enabled for emergency mode
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public boolean isEmergencyMode() {
+        return mIsEmergencyMode;
+    }
+
+    /**
+     * The builder class of {@link EnableRequestAttributes}
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final class Builder {
+        private boolean mIsEnabled;
+        private boolean mIsDemoMode = false;
+        private boolean mIsEmergencyMode = false;
+
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        public Builder(boolean isEnabled) {
+            mIsEnabled = isEnabled;
+        }
+
+        /**
+         * Set demo mode
+         *
+         * @param isDemoMode {@code true} to enable demo mode and {@code false} to disable. When
+         *                   disabling satellite, {@code isDemoMode} is always considered as
+         *                   {@code false} by Telephony.
+         * @return The builder object
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public Builder setDemoMode(boolean isDemoMode) {
+            if (mIsEnabled) {
+                mIsDemoMode = isDemoMode;
+            }
+            return this;
+        }
+
+        /**
+         * Set emergency mode
+         *
+         * @param isEmergencyMode {@code true} means satellite is enabled for emergency mode,
+         *                        {@code false} otherwise. When disabling satellite,
+         *                        {@code isEmergencyMode} is always considered as {@code false} by
+         *                        Telephony.
+         * @return The builder object
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public Builder setEmergencyMode(boolean isEmergencyMode) {
+            if (mIsEnabled) {
+                mIsEmergencyMode = isEmergencyMode;
+            }
+            return this;
+        }
+
+        /**
+         * Build the {@link EnableRequestAttributes}
+         *
+         * @return The {@link EnableRequestAttributes} instance.
+         */
+        @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+        @NonNull
+        public EnableRequestAttributes build() {
+            return new EnableRequestAttributes(this);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index b97822a..4a61114 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -162,6 +162,13 @@
 
     /**
      * Bundle key to get the response from
+     * {@link #requestIsEmergencyModeEnabled(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_EMERGENCY_MODE_ENABLED = "emergency_mode_enabled";
+
+    /**
+     * Bundle key to get the response from
      * {@link #requestIsSupported(Executor, OutcomeReceiver)}.
      * @hide
      */
@@ -341,6 +348,13 @@
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23;
 
+    /**
+     * Telephony framework timeout to receive ACK or response from the satellite modem after
+     * sending a request to the modem.
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24;
+
     /** @hide */
     @IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
             SATELLITE_RESULT_SUCCESS,
@@ -366,7 +380,8 @@
             SATELLITE_RESULT_NOT_SUPPORTED,
             SATELLITE_RESULT_REQUEST_IN_PROGRESS,
             SATELLITE_RESULT_MODEM_BUSY,
-            SATELLITE_RESULT_ILLEGAL_STATE
+            SATELLITE_RESULT_ILLEGAL_STATE,
+            SATELLITE_RESULT_MODEM_TIMEOUT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SatelliteResult {}
@@ -482,20 +497,18 @@
      * aligned with the satellite, user can send a message and also receive a reply in demo mode.
      * If enableSatellite is {@code false}, enableDemoMode has no impact on the behavior.
      *
-     * @param enableSatellite {@code true} to enable the satellite modem and
-     *                        {@code false} to disable.
-     * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
+     * @param attributes The attributes of the enable request.
      * @param executor The executor on which the error code listener will be called.
      * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
      *
      * @throws SecurityException if the caller doesn't have required permission.
-     * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
-    public void requestEnabled(boolean enableSatellite, boolean enableDemoMode,
+    public void requestEnabled(@NonNull EnableRequestAttributes attributes,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteResult @NonNull Consumer<Integer> resultListener) {
+        Objects.requireNonNull(attributes);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(resultListener);
 
@@ -509,14 +522,17 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                telephony.requestSatelliteEnabled(mSubId, enableSatellite, enableDemoMode,
-                        errorCallback);
+                telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(),
+                        attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback);
             } else {
-                throw new IllegalStateException("telephony service is null.");
+                Rlog.e(TAG, "requestEnabled() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(
+                        () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "requestSatelliteEnabled() RemoteException: ", ex);
-            ex.rethrowAsRuntimeException();
+            Rlog.e(TAG, "requestEnabled() exception: ", ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -566,12 +582,14 @@
                 };
                 telephony.requestIsSatelliteEnabled(mSubId, receiver);
             } else {
+                loge("requestIsEnabled() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestIsSatelliteEnabled() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("requestIsEnabled() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -621,11 +639,68 @@
                 };
                 telephony.requestIsDemoModeEnabled(mSubId, receiver);
             } else {
+                loge("requestIsDemoModeEnabled() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
             loge("requestIsDemoModeEnabled() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
+    /**
+     * Request to get whether the satellite service is enabled for emergency mode.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if satellite is enabled
+     *                 for emergency mode and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestIsEmergencyModeEnabled(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_EMERGENCY_MODE_ENABLED)) {
+                                boolean isEmergencyModeEnabled =
+                                        resultData.getBoolean(KEY_EMERGENCY_MODE_ENABLED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isEmergencyModeEnabled)));
+                            } else {
+                                loge("KEY_EMERGENCY_MODE_ENABLED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsEmergencyModeEnabled(mSubId, receiver);
+            } else {
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsEmergencyModeEnabled() RemoteException: " + ex);
             ex.rethrowAsRuntimeException();
         }
     }
@@ -678,12 +753,14 @@
                 };
                 telephony.requestIsSatelliteSupported(mSubId, receiver);
             } else {
+                loge("requestIsSupported() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestIsSatelliteSupported() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("requestIsSupported() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -733,12 +810,14 @@
                 };
                 telephony.requestSatelliteCapabilities(mSubId, receiver);
             } else {
+                loge("requestCapabilities() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestSatelliteCapabilities() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("requestCapabilities() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -1014,12 +1093,14 @@
                 telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
                         internalCallback);
             } else {
+                loge("startTransmissionUpdates() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
-            loge("startSatelliteTransmissionUpdates() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("startTransmissionUpdates() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1069,12 +1150,14 @@
                             () -> resultListener.accept(SATELLITE_RESULT_INVALID_ARGUMENTS)));
                 }
             } else {
+                loge("stopTransmissionUpdates() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
-            loge("stopSatelliteTransmissionUpdates() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("stopTransmissionUpdates() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1092,7 +1175,6 @@
      * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
      *
      * @throws SecurityException if the caller doesn't have required permission.
-     * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@@ -1119,12 +1201,14 @@
                 cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
                         errorCallback);
             } else {
+                loge("provisionService() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
-            loge("provisionSatelliteService() RemoteException=" + ex);
-            ex.rethrowAsRuntimeException();
+            loge("provisionService() RemoteException=" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
         if (cancellationSignal != null) {
             cancellationSignal.setRemote(cancelRemote);
@@ -1168,12 +1252,14 @@
                 };
                 telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
             } else {
+                loge("deprovisionService() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
-            loge("deprovisionSatelliteService() RemoteException=" + ex);
-            ex.rethrowAsRuntimeException();
+            loge("deprovisionService() RemoteException ex=" + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1215,7 +1301,7 @@
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("registerForSatelliteProvisionStateChanged() RemoteException: " + ex);
+            loge("registerForProvisionStateChanged() RemoteException: " + ex);
             ex.rethrowAsRuntimeException();
         }
         return SATELLITE_RESULT_REQUEST_FAILED;
@@ -1302,12 +1388,14 @@
                 };
                 telephony.requestIsSatelliteProvisioned(mSubId, receiver);
             } else {
+                loge("requestIsProvisioned() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            loge("requestIsProvisioned() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -1347,7 +1435,7 @@
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("registerForSatelliteModemStateChanged() RemoteException:" + ex);
+            loge("registerForModemStateChanged() RemoteException:" + ex);
             ex.rethrowAsRuntimeException();
         }
         return SATELLITE_RESULT_REQUEST_FAILED;
@@ -1516,12 +1604,14 @@
                 };
                 telephony.pollPendingDatagrams(mSubId, internalCallback);
             } else {
+                loge("pollPendingDatagrams() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
             loge("pollPendingDatagrams() RemoteException:" + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1573,12 +1663,14 @@
                 telephony.sendDatagram(mSubId, datagramType, datagram,
                         needFullScreenPointingUI, internalCallback);
             } else {
+                loge("sendDatagram() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
             loge("sendDatagram() RemoteException:" + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1628,16 +1720,16 @@
                         }
                     }
                 };
-                telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId,
-                        receiver);
+                telephony.requestIsCommunicationAllowedForCurrentLocation(mSubId, receiver);
             } else {
+                loge("requestIsCommunicationAllowedForCurrentLocation() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: "
-                    + ex);
-            ex.rethrowAsRuntimeException();
+            loge("requestIsCommunicationAllowedForCurrentLocation() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -1688,12 +1780,14 @@
                 };
                 telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver);
             } else {
+                loge("requestTimeForNextSatelliteVisibility() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
             loge("requestTimeForNextSatelliteVisibility() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -1720,7 +1814,7 @@
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("informDeviceAlignedToSatellite() RemoteException:" + ex);
+            loge("setDeviceAlignedWithSatellite() RemoteException:" + ex);
             ex.rethrowAsRuntimeException();
         }
     }
@@ -1830,12 +1924,14 @@
                 };
                 telephony.addAttachRestrictionForCarrier(subId, reason, errorCallback);
             } else {
+                loge("addAttachRestrictionForCarrier() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
             loge("addAttachRestrictionForCarrier() RemoteException:" + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1873,12 +1969,14 @@
                 };
                 telephony.removeAttachRestrictionForCarrier(subId, reason, errorCallback);
             } else {
+                loge("removeAttachRestrictionForCarrier() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
                         () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
             }
         } catch (RemoteException ex) {
             loge("removeAttachRestrictionForCarrier() RemoteException:" + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> resultListener.accept(SATELLITE_RESULT_ILLEGAL_STATE)));
         }
     }
 
@@ -1939,10 +2037,7 @@
      * The {@link NtnSignalStrength#NTN_SIGNAL_STRENGTH_NONE} will be returned if there is no
      * signal strength data available.
      * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} will return a
-     * {@link SatelliteException} with the {@link SatelliteResult}, or return a
-     * {@link IllegalStateException} if the Telephony process is not currently available or
-     * satellite is not supported, or return a {@link RuntimeException} when remote procedure call
-     * has failed.
+     * {@link SatelliteException} with the {@link SatelliteResult}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
@@ -1980,12 +2075,14 @@
                 };
                 telephony.requestNtnSignalStrength(mSubId, receiver);
             } else {
+                loge("requestNtnSignalStrength() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
             loge("requestNtnSignalStrength() RemoteException: " + ex);
-            ex.rethrowAsRuntimeException();
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
@@ -2187,14 +2284,11 @@
         return new ArrayList<>();
     }
 
-    private static ITelephony getITelephony() {
+    @Nullable private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
                 .getTelephonyServiceRegisterer()
                 .get());
-        if (binder == null) {
-            throw new RuntimeException("Could not find Telephony Service.");
-        }
         return binder;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 213fbc5..bd47b1f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2742,14 +2742,19 @@
      * Request to enable or disable the satellite modem.
      *
      * @param subId The subId of the subscription to enable or disable the satellite modem for.
-     * @param enable True to enable the satellite modem and false to disable.
-     * @param isDemoModeEnabled True if demo mode is enabled and false otherwise.
+     * @param enableSatellite True to enable the satellite modem and false to disable.
+     * @param enableDemoMode True if demo mode is enabled and false otherwise. When
+     *                       disabling satellite, {@code enableDemoMode} is always considered as
+     *                       {@code false} by Telephony.
+     * @param isEmergency {@code true} means satellite is enabled for emergency mode, {@code false}
+     *                    otherwise. When disabling satellite, {@code isEmergency} is always
+     *                    considered as {@code false} by Telephony.
      * @param callback The callback to get the result of the request.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestSatelliteEnabled(int subId, boolean enable, boolean isDemoModeEnabled,
-            in IIntegerConsumer callback);
+    void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+            boolean isEmergency, in IIntegerConsumer callback);
 
     /**
      * Request to get whether the satellite modem is enabled.
@@ -2775,6 +2780,18 @@
     void requestIsDemoModeEnabled(int subId, in ResultReceiver receiver);
 
     /**
+     * Request to get whether the satellite service is enabled with emergency mode.
+     *
+     * @param subId The subId of the subscription to request whether the satellite demo mode is
+     *              enabled for.
+     * @param receiver Result receiver to get the error code of the request and whether the
+     *                 satellite is enabled with emergency mode.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestIsEmergencyModeEnabled(int subId, in ResultReceiver receiver);
+
+    /**
      * Request to get whether the satellite service is supported on the device.
      *
      * @param subId The subId of the subscription to check whether satellite is supported for.
diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
index 0d61fcb..091974a 100644
--- a/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/IWwanSelectorResultCallback.aidl
@@ -16,8 +16,8 @@
 
 package com.android.internal.telephony;
 
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
 
 oneway interface IWwanSelectorResultCallback {
-    void onComplete(in EmergencyRegResult result);
+    void onComplete(in EmergencyRegistrationResult result);
 }
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index d417772..053bc7d 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -59,4 +59,5 @@
     boolean isCompatChangeEnabled(String callingPackage, long changeId);
     void setPsimConversionSupportedCarriers(in int[] carrierIds);
     boolean isPsimConversionSupported(in int carrierId);
+    long getAvailableMemoryInBytes(int cardId, String callingPackage);
 }