Merge "Add Simultaneous Calling restrictions to PhoneAccount toString" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index beb11fc..98b62b3 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -26,6 +26,7 @@
     ":android.content.flags-aconfig-java{.generated_srcjars}",
     ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
     ":android.content.res.flags-aconfig-java{.generated_srcjars}",
+    ":android.crashrecovery.flags-aconfig-java{.generated_srcjars}",
     ":android.credentials.flags-aconfig-java{.generated_srcjars}",
     ":android.database.sqlite-aconfig-java{.generated_srcjars}",
     ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
@@ -94,6 +95,7 @@
         "android.companion.virtual.flags-aconfig",
         "android.content.pm.flags-aconfig",
         "android.content.res.flags-aconfig",
+        "android.crashrecovery.flags-aconfig",
         "android.credentials.flags-aconfig",
         "android.database.sqlite-aconfig",
         "android.hardware.biometrics.flags-aconfig",
@@ -1078,3 +1080,16 @@
     aconfig_declarations: "android.adaptiveauth.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// CrashRecovery Module
+aconfig_declarations {
+    name: "android.crashrecovery.flags-aconfig",
+    package: "android.crashrecovery.flags",
+    srcs: ["packages/CrashRecovery/aconfig/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.crashrecovery.flags-aconfig-java",
+    aconfig_declarations: "android.crashrecovery.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 9c56733..e12f74f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -95,6 +95,7 @@
         ":platform-compat-native-aidl",
 
         // AIDL sources from external directories
+        ":android.frameworks.location.altitude-V2-java-source",
         ":android.hardware.biometrics.common-V4-java-source",
         ":android.hardware.biometrics.fingerprint-V3-java-source",
         ":android.hardware.biometrics.face-V4-java-source",
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
index 0ea2daf..bc84708 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -248,6 +248,11 @@
         return mServiceWatcher.waitOnCreate();
     }
 
+    /** Wait for session paused. */
+    public void waitForSessionPaused() throws InterruptedException {
+        mServiceWatcher.waitSessionPaused();
+    }
+
     @NonNull
     protected ActivityWatcher startWatcher() {
         return mActivitiesWatcher.watch(CustomTestActivity.class);
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
index aa95dfd..44e8a67 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -80,6 +80,34 @@
         testActivityLaunchTime(R.layout.test_container_activity, 500);
     }
 
+    @Test
+    public void testSendEventsLatency() throws Throwable {
+        enableService();
+
+        testSendEventLatency(R.layout.test_container_activity, 0);
+    }
+
+    @Test
+    public void testSendEventsLatency_contains100Views() throws Throwable {
+        enableService();
+
+        testSendEventLatency(R.layout.test_container_activity, 100);
+    }
+
+    @Test
+    public void testSendEventsLatency_contains300Views() throws Throwable {
+        enableService();
+
+        testSendEventLatency(R.layout.test_container_activity, 300);
+    }
+
+    @Test
+    public void testSendEventsLatency_contains500Views() throws Throwable {
+        enableService();
+
+        testSendEventLatency(R.layout.test_container_activity, 500);
+    }
+
     private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
         final Object drawNotifier = new Object();
         final Intent intent = getLaunchIntent(layoutId, numViews);
@@ -111,6 +139,38 @@
         }
     }
 
+    private void testSendEventLatency(int layoutId, int numViews) throws Throwable {
+        final Object drawNotifier = new Object();
+        final Intent intent = getLaunchIntent(layoutId, numViews);
+        intent.putExtra(CustomTestActivity.INTENT_EXTRA_FINISH_ON_IDLE, true);
+        intent.putExtra(CustomTestActivity.INTENT_EXTRA_DRAW_CALLBACK,
+                new RemoteCallback(result -> {
+                    synchronized (drawNotifier) {
+                        drawNotifier.notifyAll();
+                    }
+                }));
+        final ActivityWatcher watcher = startWatcher();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mEntryActivity.startActivity(intent);
+            synchronized (drawNotifier) {
+                try {
+                    drawNotifier.wait(GENERIC_TIMEOUT_MS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            waitForSessionPaused();
+
+            // Ignore the time to finish the activity
+            state.pauseTiming();
+            watcher.waitFor(DESTROYED);
+            sInstrumentation.waitForIdleSync();
+            state.resumeTiming();
+        }
+    }
+
     @Test
     public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
         enableService();
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
index ecc5112..0b5345f 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -114,6 +114,10 @@
     public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
             ContentCaptureEvent event) {
         Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+        if (sServiceWatcher != null
+                && event.getType() == ContentCaptureEvent.TYPE_SESSION_PAUSED) {
+            sServiceWatcher.mSessionPaused.countDown();
+        }
     }
 
     @Override
@@ -126,6 +130,7 @@
         private static final long GENERIC_TIMEOUT_MS = 10_000;
         private final CountDownLatch mCreated = new CountDownLatch(1);
         private final CountDownLatch mDestroyed = new CountDownLatch(1);
+        private final CountDownLatch mSessionPaused = new CountDownLatch(1);
         private boolean mReadyToClear = true;
         private Pair<Set<String>, Set<ComponentName>> mAllowList;
 
@@ -151,6 +156,11 @@
             await(mDestroyed, "not destroyed");
         }
 
+        /** Wait for session paused. */
+        public void waitSessionPaused() throws InterruptedException {
+            await(mSessionPaused, "no Paused");
+        }
+
         /**
          * Allow just this package.
          */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 7f5bb5c..fc193d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1840,7 +1840,9 @@
                     /* isFlexConstraintSatisfied */ false,
                     jobStatus.canApplyTransportAffinities(),
                     jobStatus.getNumAppliedFlexibleConstraints(),
-                    jobStatus.getNumDroppedFlexibleConstraints());
+                    jobStatus.getNumDroppedFlexibleConstraints(),
+                    jobStatus.getFilteredTraceTag(),
+                    jobStatus.getFilteredDebugTags());
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -2288,7 +2290,9 @@
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
                     cancelled.canApplyTransportAffinities(),
                     cancelled.getNumAppliedFlexibleConstraints(),
-                    cancelled.getNumDroppedFlexibleConstraints());
+                    cancelled.getNumDroppedFlexibleConstraints(),
+                    cancelled.getFilteredTraceTag(),
+                    cancelled.getFilteredDebugTags());
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index fe55e27..8ab7d2f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -541,7 +541,9 @@
                     job.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
                     job.canApplyTransportAffinities(),
                     job.getNumAppliedFlexibleConstraints(),
-                    job.getNumDroppedFlexibleConstraints());
+                    job.getNumDroppedFlexibleConstraints(),
+                    job.getFilteredTraceTag(),
+                    job.getFilteredDebugTags());
             sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1630,7 +1632,9 @@
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_FLEXIBLE),
                 completedJob.canApplyTransportAffinities(),
                 completedJob.getNumAppliedFlexibleConstraints(),
-                completedJob.getNumDroppedFlexibleConstraints());
+                completedJob.getNumDroppedFlexibleConstraints(),
+                completedJob.getFilteredTraceTag(),
+                completedJob.getFilteredDebugTags());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index d39863c..a4df5d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -48,6 +48,7 @@
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
+import android.util.Patterns;
 import android.util.Range;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -76,6 +77,7 @@
 import java.util.Objects;
 import java.util.Random;
 import java.util.function.Predicate;
+import java.util.regex.Pattern;
 
 /**
  * Uniquely identifies a job internally.
@@ -203,6 +205,17 @@
     // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
     private static final boolean STATS_LOG_ENABLED = false;
 
+    /**
+     * Simple patterns to match some common forms of PII. This is not intended all-encompassing and
+     * any clients should aim to do additional filtering.
+     */
+    private static final ArrayMap<Pattern, String> BASIC_PII_FILTERS = new ArrayMap<>();
+
+    static {
+        BASIC_PII_FILTERS.put(Patterns.EMAIL_ADDRESS, "[EMAIL]");
+        BASIC_PII_FILTERS.put(Patterns.PHONE, "[PHONE]");
+    }
+
     // No override.
     public static final int OVERRIDE_NONE = 0;
     // Override to improve sorting order. Does not affect constraint evaluation.
@@ -250,6 +263,18 @@
     private final long mLoggingJobId;
 
     /**
+     * List of tags from {@link JobInfo#getDebugTags()}, filtered using {@link #BASIC_PII_FILTERS}.
+     * Lazily loaded in {@link #getFilteredDebugTags()}.
+     */
+    @Nullable
+    private String[] mFilteredDebugTags;
+    /**
+     * Trace tag from {@link JobInfo#getTraceTag()}, filtered using {@link #BASIC_PII_FILTERS}.
+     * Lazily loaded in {@link #getFilteredTraceTag()}.
+     */
+    @Nullable
+    private String mFilteredTraceTag;
+    /**
      * Tag to identify the wakelock held for this job. Lazily loaded in
      * {@link #getWakelockTag()} since it's not typically needed until the job is about to run.
      */
@@ -1325,6 +1350,47 @@
         return batteryName;
     }
 
+    @VisibleForTesting
+    @NonNull
+    static String applyBasicPiiFilters(@NonNull String val) {
+        for (int i = BASIC_PII_FILTERS.size() - 1; i >= 0; --i) {
+            val = BASIC_PII_FILTERS.keyAt(i).matcher(val).replaceAll(BASIC_PII_FILTERS.valueAt(i));
+        }
+        return val;
+    }
+
+    /**
+     * List of tags from {@link JobInfo#getDebugTags()}, filtered using a basic set of PII filters.
+     */
+    @NonNull
+    public String[] getFilteredDebugTags() {
+        if (mFilteredDebugTags != null) {
+            return mFilteredDebugTags;
+        }
+        final ArraySet<String> debugTags = job.getDebugTagsArraySet();
+        mFilteredDebugTags = new String[debugTags.size()];
+        for (int i = 0; i < mFilteredDebugTags.length; ++i) {
+            mFilteredDebugTags[i] = applyBasicPiiFilters(debugTags.valueAt(i));
+        }
+        return mFilteredDebugTags;
+    }
+
+    /**
+     * Trace tag from {@link JobInfo#getTraceTag()}, filtered using a basic set of PII filters.
+     */
+    @Nullable
+    public String getFilteredTraceTag() {
+        if (mFilteredTraceTag != null) {
+            return mFilteredTraceTag;
+        }
+        final String rawTag = job.getTraceTag();
+        if (rawTag == null) {
+            return null;
+        }
+        mFilteredTraceTag = applyBasicPiiFilters(rawTag);
+        return mFilteredTraceTag;
+    }
+
     /** Return the String to be used as the tag for the wakelock held for this job. */
     @NonNull
     public String getWakelockTag() {
diff --git a/core/api/current.txt b/core/api/current.txt
index 9e3919d..66feebc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -444,6 +444,7 @@
     field public static final int alertDialogTheme = 16843529; // 0x1010309
     field public static final int alignmentMode = 16843642; // 0x101037a
     field public static final int allContactsName = 16843468; // 0x10102cc
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int allow;
     field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
@@ -847,6 +848,7 @@
     field public static final int format24Hour = 16843723; // 0x10103cb
     field public static final int fraction = 16843992; // 0x10104d8
     field public static final int fragment = 16843491; // 0x10102e3
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentAdvancedPattern;
     field public static final int fragmentAllowEnterTransitionOverlap = 16843976; // 0x10104c8
     field public static final int fragmentAllowReturnTransitionOverlap = 16843977; // 0x10104c9
     field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7
@@ -857,10 +859,13 @@
     field public static final int fragmentFadeExitAnimation = 16843498; // 0x10102ea
     field public static final int fragmentOpenEnterAnimation = 16843493; // 0x10102e5
     field public static final int fragmentOpenExitAnimation = 16843494; // 0x10102e6
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPattern;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentPrefix;
     field public static final int fragmentReenterTransition = 16843975; // 0x10104c7
     field public static final int fragmentReturnTransition = 16843973; // 0x10104c5
     field public static final int fragmentSharedElementEnterTransition = 16843972; // 0x10104c4
     field public static final int fragmentSharedElementReturnTransition = 16843974; // 0x10104c6
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int fragmentSuffix;
     field public static final int freezesText = 16843116; // 0x101016c
     field public static final int fromAlpha = 16843210; // 0x10101ca
     field public static final int fromDegrees = 16843187; // 0x10101b3
@@ -1329,10 +1334,15 @@
     field public static final int propertyYName = 16843893; // 0x1010475
     field public static final int protectionLevel = 16842761; // 0x1010009
     field public static final int publicKey = 16843686; // 0x10103a6
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int query;
     field public static final int queryActionMsg = 16843227; // 0x10101db
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryAdvancedPattern;
     field public static final int queryAfterZeroResults = 16843394; // 0x1010282
     field public static final int queryBackground = 16843911; // 0x1010487
     field public static final int queryHint = 16843608; // 0x1010358
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPattern;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int queryPrefix;
+    field @FlaggedApi("android.content.pm.relative_reference_intent_filters") public static final int querySuffix;
     field public static final int quickContactBadgeStyleSmallWindowLarge = 16843443; // 0x10102b3
     field public static final int quickContactBadgeStyleSmallWindowMedium = 16843442; // 0x10102b2
     field public static final int quickContactBadgeStyleSmallWindowSmall = 16843441; // 0x10102b1
@@ -11387,10 +11397,12 @@
     method public final void addDataScheme(String);
     method public final void addDataSchemeSpecificPart(String, int);
     method public final void addDataType(String) throws android.content.IntentFilter.MalformedMimeTypeException;
+    method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final void addUriRelativeFilterGroup(@NonNull android.content.UriRelativeFilterGroup);
     method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicate();
     method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicateWithTypeResolution(@NonNull android.content.ContentResolver);
     method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
     method public final java.util.Iterator<java.lang.String> categoriesIterator();
+    method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final void clearUriRelativeFilterGroups();
     method public final int countActions();
     method public final int countCategories();
     method public final int countDataAuthorities();
@@ -11398,6 +11410,7 @@
     method public final int countDataSchemeSpecificParts();
     method public final int countDataSchemes();
     method public final int countDataTypes();
+    method @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final int countUriRelativeFilterGroups();
     method public static android.content.IntentFilter create(String, String);
     method public final int describeContents();
     method public void dump(android.util.Printer, String);
@@ -11409,6 +11422,7 @@
     method public final android.os.PatternMatcher getDataSchemeSpecificPart(int);
     method public final String getDataType(int);
     method public final int getPriority();
+    method @FlaggedApi("android.content.pm.relative_reference_intent_filters") @NonNull public final android.content.UriRelativeFilterGroup getUriRelativeFilterGroup(int);
     method public final boolean hasAction(String);
     method public final boolean hasCategory(String);
     method public final boolean hasDataAuthority(android.net.Uri);
@@ -11830,6 +11844,27 @@
     field public static final long INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
   }
 
+  @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final class UriRelativeFilter {
+    ctor public UriRelativeFilter(int, int, @NonNull String);
+    method @NonNull public String getFilter();
+    method public int getPatternType();
+    method public int getUriPart();
+    method public boolean matchData(@NonNull android.net.Uri);
+    field public static final int FRAGMENT = 2; // 0x2
+    field public static final int PATH = 0; // 0x0
+    field public static final int QUERY = 1; // 0x1
+  }
+
+  @FlaggedApi("android.content.pm.relative_reference_intent_filters") public final class UriRelativeFilterGroup {
+    ctor public UriRelativeFilterGroup(int);
+    method public void addUriRelativeFilter(@NonNull android.content.UriRelativeFilter);
+    method public int getAction();
+    method @NonNull public java.util.Collection<android.content.UriRelativeFilter> getUriRelativeFilters();
+    method public boolean matchData(@NonNull android.net.Uri);
+    field public static final int ACTION_ALLOW = 0; // 0x0
+    field public static final int ACTION_BLOCK = 1; // 0x1
+  }
+
 }
 
 package android.content.om {
@@ -33470,6 +33505,7 @@
     field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi";
     field public static final String DISALLOW_SMS = "no_sms";
     field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+    field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
     field public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio";
     field public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
     field public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
@@ -53666,13 +53702,13 @@
     method @Deprecated public android.view.Display getDefaultDisplay();
     method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
     method public default boolean isCrossWindowBlurEnabled();
-    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver);
+    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver);
     method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.os.IBinder registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver);
+    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver);
     method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
     method public void removeViewImmediate(android.view.View);
-    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.os.IBinder);
+    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.view.SurfaceControl);
     method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
     field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
     field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea008ac..017de17 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -129,6 +129,7 @@
     field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS";
     field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
     field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT";
+    field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
     field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
@@ -4231,6 +4232,7 @@
   public final class UserProperties implements android.os.Parcelable {
     method public int describeContents();
     method public int getCrossProfileContentSharingStrategy();
+    method @FlaggedApi("android.multiuser.support_hiding_profiles") @NonNull public int getProfileApiVisibility();
     method public int getShowInQuietMode();
     method public int getShowInSharingSurfaces();
     method public boolean isCredentialShareableWithParent();
@@ -4240,6 +4242,9 @@
     field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
     field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
     field public static final int CROSS_PROFILE_CONTENT_SHARING_UNKNOWN = -1; // 0xffffffff
+    field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_HIDDEN = 1; // 0x1
+    field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1; // 0xffffffff
+    field @FlaggedApi("android.multiuser.support_hiding_profiles") public static final int PROFILE_API_VISIBILITY_VISIBLE = 0; // 0x0
     field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
     field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
     field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b8b98a3..7a3d320 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3646,6 +3646,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method public default int getDisplayImePolicy(int);
+    method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @Nullable public default android.os.IBinder getSurfaceControlInputClientToken(@NonNull android.view.SurfaceControl);
     method public static boolean hasWindowExtensionsEnabled();
     method public default void holdLock(android.os.IBinder, int);
     method public default boolean isGlobalKey(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1bdbd4c5..5d2a26e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -319,6 +319,10 @@
     public static final int SERVICE_DONE_EXECUTING_START = 1;
     /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
     public static final int SERVICE_DONE_EXECUTING_STOP = 2;
+    /** Type for IActivityManager.serviceDoneExecuting: done with an onRebind call */
+    public static final int SERVICE_DONE_EXECUTING_REBIND = 3;
+    /** Type for IActivityManager.serviceDoneExecuting: done with an onUnbind call */
+    public static final int SERVICE_DONE_EXECUTING_UNBIND = 4;
 
     /** Use foreground GC policy (less pause time) and higher JIT weight. */
     private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
@@ -4882,7 +4886,7 @@
             mServices.put(data.token, service);
             try {
                 ActivityManager.getService().serviceDoneExecuting(
-                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
+                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0, null);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4913,7 +4917,7 @@
                     } else {
                         s.onRebind(data.intent);
                         ActivityManager.getService().serviceDoneExecuting(
-                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
+                                data.token, SERVICE_DONE_EXECUTING_REBIND, 0, 0, data.intent);
                     }
                 } catch (RemoteException ex) {
                     throw ex.rethrowFromSystemServer();
@@ -4943,7 +4947,7 @@
                                 data.token, data.intent, doRebind);
                     } else {
                         ActivityManager.getService().serviceDoneExecuting(
-                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
+                                data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent);
                     }
                 } catch (RemoteException ex) {
                     throw ex.rethrowFromSystemServer();
@@ -5057,7 +5061,7 @@
 
                 try {
                     ActivityManager.getService().serviceDoneExecuting(
-                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
+                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res, null);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -5089,7 +5093,7 @@
 
                 try {
                     ActivityManager.getService().serviceDoneExecuting(
-                            token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
+                            token, SERVICE_DONE_EXECUTING_STOP, 0, 0, null);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 47403d2..b063d04 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -294,7 +294,8 @@
     @UnsupportedAppUsage
     ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     @UnsupportedAppUsage
-    oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
+    oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res,
+            in Intent intent);
     /** @deprecated  Use {@link #getIntentSenderWithFeature} instead */
     @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link PendingIntent#getIntentSender()} instead")
     IIntentSender getIntentSender(int type, in String packageName, in IBinder token,
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index ad3acd7..79af65a 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,11 +16,13 @@
 
 package android.content;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.Flags;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -175,6 +177,7 @@
     private static final String ACTION_STR = "action";
     private static final String AUTO_VERIFY_STR = "autoVerify";
     private static final String EXTRAS_STR = "extras";
+    private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup";
 
     private static final int[] EMPTY_INT_ARRAY = new int[0];
     private static final long[] EMPTY_LONG_ARRAY = new long[0];
@@ -324,6 +327,7 @@
     private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
     private ArrayList<AuthorityEntry> mDataAuthorities = null;
     private ArrayList<PatternMatcher> mDataPaths = null;
+    private ArrayList<UriRelativeFilterGroup> mUriRelativeFilterGroups = null;
     private ArrayList<String> mStaticDataTypes = null;
     private ArrayList<String> mDataTypes = null;
     private ArrayList<String> mMimeGroups = null;
@@ -520,6 +524,10 @@
         if (o.mDataPaths != null) {
             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
         }
+        if (o.mUriRelativeFilterGroups != null) {
+            mUriRelativeFilterGroups =
+                    new ArrayList<UriRelativeFilterGroup>(o.mUriRelativeFilterGroups);
+        }
         if (o.mMimeGroups != null) {
             mMimeGroups = new ArrayList<String>(o.mMimeGroups);
         }
@@ -1563,6 +1571,63 @@
     }
 
     /**
+     * Add a new URI relative filter group to match against the Intent data.  The
+     * intent filter must include one or more schemes (via {@link #addDataScheme})
+     * <em>and</em> one or more authorities (via {@link #addDataAuthority}) for
+     * the group to be considered.
+     *
+     * <p>Groups will be matched in the order they were added and matching will only
+     * be done if no data paths match or if none are included. If both data paths and
+     * groups are not included, then only the scheme/authority must match.</p>
+     *
+     * @param group A {@link UriRelativeFilterGroup} to match the URI.
+     *
+     * @see UriRelativeFilterGroup
+     * @see #matchData
+     * @see #addDataScheme
+     * @see #addDataAuthority
+     */
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    public final void addUriRelativeFilterGroup(@NonNull UriRelativeFilterGroup group) {
+        Objects.requireNonNull(group);
+        if (mUriRelativeFilterGroups == null) {
+            mUriRelativeFilterGroups = new ArrayList<>();
+        }
+        mUriRelativeFilterGroups.add(group);
+    }
+
+    /**
+     * Return the number of URI relative filter groups in the intent filter.
+     */
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    public final int countUriRelativeFilterGroups() {
+        return mUriRelativeFilterGroups == null ? 0 : mUriRelativeFilterGroups.size();
+    }
+
+    /**
+     * Return a URI relative filter group in the intent filter.
+     *
+     * <p>Note: use of this method will result in a NullPointerException
+     * if no groups exists for this intent filter.</p>
+     *
+     * @param index index of the element to return
+     * @throws IndexOutOfBoundsException if index is out of range
+     */
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    @NonNull
+    public final UriRelativeFilterGroup getUriRelativeFilterGroup(int index) {
+        return mUriRelativeFilterGroups.get(index);
+    }
+
+    /**
+     * Removes all existing URI relative filter groups in the intent filter.
+     */
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    public final void clearUriRelativeFilterGroups() {
+        mUriRelativeFilterGroups = null;
+    }
+
+    /**
      * Match this intent filter against the given Intent data.  This ignores
      * the data scheme -- unlike {@link #matchData}, the authority will match
      * regardless of whether there is a matching scheme.
@@ -1677,12 +1742,24 @@
                     int authMatch = matchDataAuthority(data, wildcardSupported);
                     if (authMatch >= 0) {
                         final ArrayList<PatternMatcher> paths = mDataPaths;
-                        if (paths == null) {
-                            match = authMatch;
-                        } else if (hasDataPath(data.getPath(), wildcardSupported)) {
-                            match = MATCH_CATEGORY_PATH;
+                        final ArrayList<UriRelativeFilterGroup> groups = mUriRelativeFilterGroups;
+                        if (Flags.relativeReferenceIntentFilters()) {
+                            if (paths == null && groups == null) {
+                                match = authMatch;
+                            } else if (hasDataPath(data.getPath(), wildcardSupported)
+                                    || matchRelRefGroups(data)) {
+                                match = MATCH_CATEGORY_PATH;
+                            } else {
+                                return NO_MATCH_DATA;
+                            }
                         } else {
-                            return NO_MATCH_DATA;
+                            if (paths == null) {
+                                match = authMatch;
+                            } else if (hasDataPath(data.getPath(), wildcardSupported)) {
+                                match = MATCH_CATEGORY_PATH;
+                            } else {
+                                return NO_MATCH_DATA;
+                            }
                         }
                     } else {
                         return NO_MATCH_DATA;
@@ -1726,6 +1803,19 @@
         return match + MATCH_ADJUSTMENT_NORMAL;
     }
 
+    private boolean matchRelRefGroups(Uri data) {
+        if (mUriRelativeFilterGroups == null) {
+            return false;
+        }
+        for (int i = 0; i < mUriRelativeFilterGroups.size(); i++) {
+            UriRelativeFilterGroup group = mUriRelativeFilterGroups.get(i);
+            if (group.matchData(data)) {
+                return group.getAction() == UriRelativeFilterGroup.ACTION_ALLOW;
+            }
+        }
+        return false;
+    }
+
     /**
      * Add a new Intent category to match against.  The semantics of
      * categories is the opposite of actions -- an Intent includes the
@@ -2486,6 +2576,12 @@
             }
             serializer.endTag(null, EXTRAS_STR);
         }
+        if (Flags.relativeReferenceIntentFilters()) {
+            N = countUriRelativeFilterGroups();
+            for (int i = 0; i < N; i++) {
+                mUriRelativeFilterGroups.get(i).writeToXml(serializer);
+            }
+        }
     }
 
     /**
@@ -2614,6 +2710,9 @@
                 }
             } else if (tagName.equals(EXTRAS_STR)) {
                 mExtras = PersistableBundle.restoreFromXml(parser);
+            } else if (Flags.relativeReferenceIntentFilters()
+                    && URI_RELATIVE_FILTER_GROUP_STR.equals(tagName)) {
+                addUriRelativeFilterGroup(new UriRelativeFilterGroup(parser));
             } else {
                 Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
             }
@@ -2680,6 +2779,12 @@
         if (mExtras != null) {
             mExtras.dumpDebug(proto, IntentFilterProto.EXTRAS);
         }
+        if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) {
+            Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator();
+            while (it.hasNext()) {
+                it.next().dumpDebug(proto, IntentFilterProto.URI_RELATIVE_FILTER_GROUPS);
+            }
+        }
         proto.end(token);
     }
 
@@ -2744,6 +2849,15 @@
                 du.println(sb.toString());
             }
         }
+        if (mUriRelativeFilterGroups != null) {
+            Iterator<UriRelativeFilterGroup> it = mUriRelativeFilterGroups.iterator();
+            while (it.hasNext()) {
+                sb.setLength(0);
+                sb.append(prefix); sb.append("UriRelativeFilterGroup: \"");
+                sb.append(it.next()); sb.append("\"");
+                du.println(sb.toString());
+            }
+        }
         if (mStaticDataTypes != null) {
             Iterator<String> it = mStaticDataTypes.iterator();
             while (it.hasNext()) {
@@ -2883,6 +2997,15 @@
         } else {
             dest.writeInt(0);
         }
+        if (Flags.relativeReferenceIntentFilters() && mUriRelativeFilterGroups != null) {
+            final int N = mUriRelativeFilterGroups.size();
+            dest.writeInt(N);
+            for (int i = 0; i < N; i++) {
+                mUriRelativeFilterGroups.get(i).writeToParcel(dest, flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     /**
@@ -2989,6 +3112,13 @@
         if (source.readInt() != 0) {
             mExtras = PersistableBundle.CREATOR.createFromParcel(source);
         }
+        N = source.readInt();
+        if (Flags.relativeReferenceIntentFilters() && N > 0) {
+            mUriRelativeFilterGroups = new ArrayList<UriRelativeFilterGroup>(N);
+            for (int i = 0; i < N; i++) {
+                mUriRelativeFilterGroups.add(new UriRelativeFilterGroup(source));
+            }
+        }
     }
 
     private boolean hasPartialTypes() {
diff --git a/core/java/android/content/UriRelativeFilter.java b/core/java/android/content/UriRelativeFilter.java
new file mode 100644
index 0000000..9866cd0
--- /dev/null
+++ b/core/java/android/content/UriRelativeFilter.java
@@ -0,0 +1,260 @@
+/*
+ * 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.content;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.Flags;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.PatternMatcher;
+import android.util.proto.ProtoOutputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A filter for matching Intent URI Data as part of a
+ * {@link UriRelativeFilterGroup}. A single filter can only be
+ * matched against either a URI path, query or fragment
+ */
+@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+public final class UriRelativeFilter {
+    private static final String FILTER_STR = "filter";
+    private static final String PART_STR = "part";
+    private static final String PATTERN_STR = "pattern";
+    static final String URI_RELATIVE_FILTER_STR = "uriRelativeFilter";
+
+    /**
+     * Value to indicate that the filter is to be applied to a URI path.
+     */
+    public static final int PATH = 0;
+    /**
+     * Value to indicate that the filter is to be applied to a URI query.
+     */
+    public static final int QUERY = 1;
+    /**
+     * Value to indicate that the filter is to be applied to a URI fragment.
+     */
+    public static final int FRAGMENT = 2;
+
+    /** @hide */
+    @IntDef(value = {
+            PATH,
+            QUERY,
+            FRAGMENT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UriPart {}
+
+    private final @UriPart int mUriPart;
+    private final @PatternMatcher.PatternType int mPatternType;
+    private final String mFilter;
+
+    /**
+     * Creates a new UriRelativeFilter.
+     *
+     * @param uriPart The URI part this filter operates on. Can be either a
+     *                {@link UriRelativeFilter#PATH}, {@link UriRelativeFilter#QUERY},
+     *                or {@link UriRelativeFilter#FRAGMENT}.
+     * @param patternType The pattern type of the filter. Can be either a
+     *                    {@link PatternMatcher#PATTERN_LITERAL},
+     *                    {@link PatternMatcher#PATTERN_PREFIX},
+*                         {@link PatternMatcher#PATTERN_SUFFIX},
+     *                    {@link PatternMatcher#PATTERN_SIMPLE_GLOB},
+     *                    or {@link PatternMatcher#PATTERN_ADVANCED_GLOB}.
+     * @param filter A literal or pattern string depedning on patterType
+     *               used to match a uriPart .
+     */
+    public UriRelativeFilter(
+            @UriPart int uriPart,
+            @PatternMatcher.PatternType int patternType,
+            @NonNull String filter) {
+        mUriPart = uriPart;
+        com.android.internal.util.AnnotationValidations.validate(
+                UriPart.class, null, mUriPart);
+        mPatternType = patternType;
+        com.android.internal.util.AnnotationValidations.validate(
+                PatternMatcher.PatternType.class, null, mPatternType);
+        mFilter = filter;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mFilter);
+    }
+
+    /**
+     * The URI part this filter operates on.
+     */
+    public @UriPart int getUriPart() {
+        return mUriPart;
+    }
+
+    /**
+     * The pattern type of the filter.
+     */
+    public @PatternMatcher.PatternType int getPatternType() {
+        return mPatternType;
+    }
+
+    /**
+     * The string used to filter the URI.
+     */
+    public @NonNull String getFilter() {
+        return mFilter;
+    }
+
+    /**
+     * Match this URI filter against an Intent's data. QUERY filters can
+     * match against any key value pair in the query string. PATH and
+     * FRAGMENT filters must match the entire string.
+     *
+     * @param data The full data string to match against, as supplied in
+     *             Intent.data.
+     *
+     * @return true if there is a match.
+     */
+    public boolean matchData(@NonNull Uri data) {
+        PatternMatcher pe = new PatternMatcher(mFilter, mPatternType);
+        switch (getUriPart()) {
+            case PATH:
+                return pe.match(data.getPath());
+            case QUERY:
+                return matchQuery(pe, data.getQuery());
+            case FRAGMENT:
+                return pe.match(data.getFragment());
+            default:
+                return false;
+        }
+    }
+
+    private boolean matchQuery(PatternMatcher pe, String query) {
+        if (query != null) {
+            String[] params = query.split("&");
+            if (params.length == 1) {
+                params = query.split(";");
+            }
+            for (int i = 0; i < params.length; i++) {
+                if (pe.match(params[i])) return true;
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(UriRelativeFilterProto.URI_PART, mUriPart);
+        proto.write(UriRelativeFilterProto.PATTERN_TYPE, mPatternType);
+        proto.write(UriRelativeFilterProto.FILTER, mFilter);
+        proto.end(token);
+    }
+
+    /** @hide */
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.startTag(null, URI_RELATIVE_FILTER_STR);
+        serializer.attribute(null, PATTERN_STR, Integer.toString(mPatternType));
+        serializer.attribute(null, PART_STR, Integer.toString(mUriPart));
+        serializer.attribute(null, FILTER_STR, mFilter);
+        serializer.endTag(null, URI_RELATIVE_FILTER_STR);
+    }
+
+    private String uriPartToString() {
+        switch (mUriPart) {
+            case PATH:
+                return "PATH";
+            case QUERY:
+                return "QUERY";
+            case FRAGMENT:
+                return "FRAGMENT";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    private String patternTypeToString() {
+        switch (mPatternType) {
+            case PatternMatcher.PATTERN_LITERAL:
+                return "LITERAL";
+            case PatternMatcher.PATTERN_PREFIX:
+                return "PREFIX";
+            case PatternMatcher.PATTERN_SIMPLE_GLOB:
+                return "GLOB";
+            case PatternMatcher.PATTERN_ADVANCED_GLOB:
+                return "ADVANCED_GLOB";
+            case PatternMatcher.PATTERN_SUFFIX:
+                return "SUFFIX";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "UriRelativeFilter { "
+                + "uriPart = " + uriPartToString() + ", "
+                + "patternType = " + patternTypeToString() + ", "
+                + "filter = " + mFilter
+                + " }";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        UriRelativeFilter that = (UriRelativeFilter) o;
+        return mUriPart == that.mUriPart
+                && mPatternType == that.mPatternType
+                && java.util.Objects.equals(mFilter, that.mFilter);
+    }
+
+    @Override
+    public int hashCode() {
+        int _hash = 1;
+        _hash = 31 * _hash + mUriPart;
+        _hash = 31 * _hash + mPatternType;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mFilter);
+        return _hash;
+    }
+
+    /** @hide */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mUriPart);
+        dest.writeInt(mPatternType);
+        dest.writeString(mFilter);
+    }
+
+    /** @hide */
+    UriRelativeFilter(@NonNull android.os.Parcel in) {
+        mUriPart = in.readInt();
+        mPatternType = in.readInt();
+        mFilter = in.readString();
+    }
+
+    /** @hide */
+    public UriRelativeFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
+        mUriPart = Integer.parseInt(parser.getAttributeValue(null, PART_STR));
+        mPatternType = Integer.parseInt(parser.getAttributeValue(null, PATTERN_STR));
+        mFilter = parser.getAttributeValue(null, FILTER_STR);
+    }
+}
diff --git a/core/java/android/content/UriRelativeFilterGroup.java b/core/java/android/content/UriRelativeFilterGroup.java
new file mode 100644
index 0000000..72c396a
--- /dev/null
+++ b/core/java/android/content/UriRelativeFilterGroup.java
@@ -0,0 +1,216 @@
+/*
+ * 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.content;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.pm.Flags;
+import android.net.Uri;
+import android.os.Parcel;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * An intent data matching group based on a URI's relative reference which
+ * includes the path, query and fragment.  The group is only considered as
+ * matching if <em>all</em> UriRelativeFilters in the group match.  Each
+ * UriRelativeFilter defines a matching rule for a URI path, query or fragment.
+ * A group must contain one or more UriRelativeFilters to match but does not need to
+ * contain UriRelativeFilters for all existing parts of a URI to match.
+ *
+ * <p>For example, given a URI that contains path, query and fragment parts,
+ * a group containing only a path filter will match the URI if the path
+ * filter matches the URI path.  If the group contains a path and query
+ * filter, then the group will only match if both path and query filters
+ * match.  If a URI contains only a path with no query or fragment then a
+ * group can only match if it contains only a matching path filter. If the
+ * group also contained additional query or fragment filters then it will
+ * not match.</p>
+ */
+@FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+public final class UriRelativeFilterGroup {
+    private static final String ALLOW_STR = "allow";
+    private static final String URI_RELATIVE_FILTER_GROUP_STR = "uriRelativeFilterGroup";
+
+    /**
+     * Value to indicate that the group match is allowed.
+     */
+    public static final int ACTION_ALLOW = 0;
+    /**
+     * Value to indicate that the group match is blocked.
+     */
+    public static final int ACTION_BLOCK = 1;
+
+    /** @hide */
+    @IntDef(value = {
+            ACTION_ALLOW,
+            ACTION_BLOCK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Action {}
+
+    private final @Action int mAction;
+    private final ArraySet<UriRelativeFilter> mUriRelativeFilters = new ArraySet<>();
+
+    /**
+     * New UriRelativeFilterGroup that matches a Intent data.
+     *
+     * @param action Whether this matching group should be allowed or disallowed.
+     */
+    public UriRelativeFilterGroup(@Action int action) {
+        mAction = action;
+    }
+
+    /** @hide */
+    public UriRelativeFilterGroup(XmlPullParser parser) throws XmlPullParserException, IOException {
+        mAction = Integer.parseInt(parser.getAttributeValue(null, ALLOW_STR));
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(UriRelativeFilter.URI_RELATIVE_FILTER_STR)) {
+                addUriRelativeFilter(new UriRelativeFilter(parser));
+            } else {
+                Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+
+    /**
+     * Return {@link UriRelativeFilterGroup#ACTION_ALLOW} if a URI is allowed when matched
+     * and {@link UriRelativeFilterGroup#ACTION_BLOCK} if a URI is blacked when matched.
+     */
+    public @Action int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Add a filter to the group.
+     */
+    public void addUriRelativeFilter(@NonNull UriRelativeFilter uriRelativeFilter) {
+        Objects.requireNonNull(uriRelativeFilter);
+        if (!CollectionUtils.contains(mUriRelativeFilters, uriRelativeFilter)) {
+            mUriRelativeFilters.add(uriRelativeFilter);
+        }
+    }
+
+    /**
+     * Returns a unmodifiable view of the UriRelativeFilters list in this group.
+     */
+    @NonNull
+    public Collection<UriRelativeFilter> getUriRelativeFilters() {
+        return Collections.unmodifiableCollection(mUriRelativeFilters);
+    }
+
+    /**
+     * Match all URI filter in this group against {@link Intent#getData()}.
+     *
+     * @param data The full data string to match against, as supplied in
+     *             Intent.data.
+     * @return true if all filters match.
+     */
+    public boolean matchData(@NonNull Uri data) {
+        if (mUriRelativeFilters.size() == 0) {
+            return false;
+        }
+        for (UriRelativeFilter filter : mUriRelativeFilters) {
+            if (!filter.matchData(data)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** @hide */
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(UriRelativeFilterGroupProto.ACTION, mAction);
+        Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator();
+        while (it.hasNext()) {
+            it.next().dumpDebug(proto, UriRelativeFilterGroupProto.URI_RELATIVE_FILTERS);
+        }
+        proto.end(token);
+    }
+
+    /** @hide */
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.startTag(null, URI_RELATIVE_FILTER_GROUP_STR);
+        serializer.attribute(null, ALLOW_STR, Integer.toString(mAction));
+        Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator();
+        while (it.hasNext()) {
+            UriRelativeFilter filter = it.next();
+            filter.writeToXml(serializer);
+        }
+        serializer.endTag(null, URI_RELATIVE_FILTER_GROUP_STR);
+    }
+
+    @Override
+    public String toString() {
+        return "UriRelativeFilterGroup { allow = " + mAction
+                + ", uri_filters = " + mUriRelativeFilters + ",  }";
+    }
+
+    /** @hide */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mAction);
+        final int n = mUriRelativeFilters.size();
+        if (n > 0) {
+            dest.writeInt(n);
+            Iterator<UriRelativeFilter> it = mUriRelativeFilters.iterator();
+            while (it.hasNext()) {
+                it.next().writeToParcel(dest, flags);
+            }
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    /** @hide */
+    UriRelativeFilterGroup(@NonNull Parcel src) {
+        mAction = src.readInt();
+        final int n = src.readInt();
+        for (int i = 0; i < n; i++) {
+            mUriRelativeFilters.add(new UriRelativeFilter(src));
+        }
+    }
+}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 269c6c2..1d0e2db 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -16,6 +16,9 @@
 
 package android.content.pm;
 
+import static android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -73,7 +76,7 @@
 
     private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY =
             "crossProfileContentSharingStrategy";
-
+    private static final String ATTR_PROFILE_API_VISIBILITY = "profileApiVisibility";
     /** Index values of each property (to indicate whether they are present in this object). */
     @IntDef(prefix = "INDEX_", value = {
             INDEX_SHOW_IN_LAUNCHER,
@@ -93,6 +96,7 @@
             INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE,
             INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
             INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING,
+            INDEX_PROFILE_API_VISIBILITY
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface PropertyIndex {
@@ -114,6 +118,7 @@
     private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14;
     private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15;
     private static final int INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = 16;
+    private static final int INDEX_PROFILE_API_VISIBILITY = 17;
     /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
     private long mPropertiesPresent = 0;
 
@@ -450,6 +455,41 @@
     @SuppressLint("UnflaggedApi") // b/306636213
     public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1;
 
+    /**
+     * Possible values for the profile visibility in public API surfaces. This indicates whether or
+     * not the information linked to the profile (userId, package names) should not be returned in
+     * API surfaces if a user is marked as hidden.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "PROFILE_API_VISIBILITY_",
+            value = {
+                    PROFILE_API_VISIBILITY_UNKNOWN,
+                    PROFILE_API_VISIBILITY_VISIBLE,
+                    PROFILE_API_VISIBILITY_HIDDEN,
+            }
+    )
+    public @interface ProfileApiVisibility {
+    }
+    /*
+    * The api visibility value for this profile user is undefined or unknown.
+     */
+    @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+    public static final int PROFILE_API_VISIBILITY_UNKNOWN = -1;
+
+    /**
+     * Indicates that information about this profile user should be shown in API surfaces.
+     */
+    @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+    public static final int PROFILE_API_VISIBILITY_VISIBLE = 0;
+
+    /**
+     * Indicates that information about this profile should be not be visible in API surfaces.
+     */
+    @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+    public static final int PROFILE_API_VISIBILITY_HIDDEN = 1;
+
 
     /**
      * Creates a UserProperties (intended for the SystemServer) that stores a reference to the given
@@ -510,6 +550,9 @@
         setShowInQuietMode(orig.getShowInQuietMode());
         setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
         setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
+        if (android.multiuser.Flags.supportHidingProfiles()) {
+            setProfileApiVisibility(orig.getProfileApiVisibility());
+        }
     }
 
     /**
@@ -951,9 +994,31 @@
     }
     private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy;
 
+    /**
+     * Returns the visibility of the profile user in API surfaces. Any information linked to the
+     * profile (userId, package names) should be hidden API surfaces if a user is marked as hidden.
+     */
+    @NonNull
+    @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+    public @ProfileApiVisibility int getProfileApiVisibility() {
+        if (isPresent(INDEX_PROFILE_API_VISIBILITY)) return mProfileApiVisibility;
+        if (mDefaultProperties != null) return mDefaultProperties.mProfileApiVisibility;
+        throw new SecurityException("You don't have permission to query profileApiVisibility");
+    }
+    /** @hide */
+    @NonNull
+    @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+    public void setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility) {
+        this.mProfileApiVisibility = profileApiVisibility;
+        setPresent(INDEX_PROFILE_API_VISIBILITY);
+    }
+    private @ProfileApiVisibility int mProfileApiVisibility;
 
     @Override
     public String toString() {
+        String profileApiVisibility =
+                android.multiuser.Flags.supportHidingProfiles() ? ", mProfileApiVisibility="
+                        + getProfileApiVisibility() : "";
         // Please print in increasing order of PropertyIndex.
         return "UserProperties{"
                 + "mPropertiesPresent=" + Long.toBinaryString(mPropertiesPresent)
@@ -977,6 +1042,7 @@
                 + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
                 + ", mAlwaysVisible=" + getAlwaysVisible()
                 + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy()
+                + profileApiVisibility
                 + "}";
     }
 
@@ -1010,6 +1076,9 @@
         pw.println(prefix + "    mAlwaysVisible=" + getAlwaysVisible());
         pw.println(prefix + "    mCrossProfileContentSharingStrategy="
                 + getCrossProfileContentSharingStrategy());
+        if (android.multiuser.Flags.supportHidingProfiles()) {
+            pw.println(prefix + "    mProfileApiVisibility=" + getProfileApiVisibility());
+        }
     }
 
     /**
@@ -1093,6 +1162,12 @@
                     break;
                 case ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY:
                     setCrossProfileContentSharingStrategy(parser.getAttributeInt(i));
+                    break;
+                case ATTR_PROFILE_API_VISIBILITY:
+                    if (android.multiuser.Flags.supportHidingProfiles()) {
+                        setProfileApiVisibility(parser.getAttributeInt(i));
+                    }
+                    break;
                 default:
                     Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
             }
@@ -1175,6 +1250,12 @@
             serializer.attributeInt(null, ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY,
                     mCrossProfileContentSharingStrategy);
         }
+        if (isPresent(INDEX_PROFILE_API_VISIBILITY)) {
+            if (android.multiuser.Flags.supportHidingProfiles()) {
+                serializer.attributeInt(null, ATTR_PROFILE_API_VISIBILITY,
+                        mProfileApiVisibility);
+            }
+        }
     }
 
     // For use only with an object that has already had any permission-lacking fields stripped out.
@@ -1198,6 +1279,7 @@
         dest.writeBoolean(mDeleteAppWithParent);
         dest.writeBoolean(mAlwaysVisible);
         dest.writeInt(mCrossProfileContentSharingStrategy);
+        dest.writeInt(mProfileApiVisibility);
     }
 
     /**
@@ -1225,6 +1307,7 @@
         mDeleteAppWithParent = source.readBoolean();
         mAlwaysVisible = source.readBoolean();
         mCrossProfileContentSharingStrategy = source.readInt();
+        mProfileApiVisibility = source.readInt();
     }
 
     @Override
@@ -1274,6 +1357,7 @@
         private boolean mAlwaysVisible = false;
         private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy =
                 CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION;
+        private @ProfileApiVisibility int mProfileApiVisibility = 0;
 
         /**
          * @hide
@@ -1428,6 +1512,17 @@
             return this;
         }
 
+        /**
+         * Sets the value for {@link #mProfileApiVisibility}
+         * @hide
+         */
+        @NonNull
+        @FlaggedApi(FLAG_SUPPORT_HIDING_PROFILES)
+        public Builder setProfileApiVisibility(@ProfileApiVisibility int profileApiVisibility){
+            mProfileApiVisibility = profileApiVisibility;
+            return this;
+        }
+
         /** Builds a UserProperties object with *all* values populated.
          * @hide
          */
@@ -1452,7 +1547,8 @@
                     mAllowStoppingUserWithDelayedLocking,
                     mDeleteAppWithParent,
                     mAlwaysVisible,
-                    mCrossProfileContentSharingStrategy);
+                    mCrossProfileContentSharingStrategy,
+                    mProfileApiVisibility);
         }
     } // end Builder
 
@@ -1473,7 +1569,8 @@
             boolean allowStoppingUserWithDelayedLocking,
             boolean deleteAppWithParent,
             boolean alwaysVisible,
-            @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) {
+            @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy,
+            @ProfileApiVisibility int profileApiVisibility) {
         mDefaultProperties = null;
         setShowInLauncher(showInLauncher);
         setStartWithParent(startWithParent);
@@ -1493,5 +1590,8 @@
         setDeleteAppWithParent(deleteAppWithParent);
         setAlwaysVisible(alwaysVisible);
         setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy);
+        if (android.multiuser.Flags.supportHidingProfiles()) {
+            setProfileApiVisibility(profileApiVisibility);
+        }
     }
 }
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index fd87290..caff457 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -110,6 +110,14 @@
 }
 
 flag {
+    name: "relative_reference_intent_filters"
+    namespace: "package_manager_service"
+    description: "Feature flag to enable relative reference intent filters"
+    bug: "307556883"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "fix_duplicated_flags"
     namespace: "package_manager_service"
     description: "Feature flag to fix duplicated PackageManager flag values"
@@ -168,3 +176,10 @@
     description: "Feature flag to allow the sandbox SDK to query intent activities of the client app."
     bug: "295842134"
 }
+
+flag {
+    name: "emergency_install_permission"
+    namespace: "permissions"
+    description: "Feature flag to enable permission EMERGENCY_INSTALL_PACKAGES"
+    bug: "321080601"
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index c083437..efb8607 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -100,3 +100,11 @@
     description: "Enable only the API changes to support private space"
     bug: "299069460"
 }
+
+flag {
+    name: "support_hiding_profiles"
+    namespace: "profile_experiences"
+    description: "Allow the use of a hide_profile property to hide some profiles behind a permission"
+    bug: "316362775"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/os/PatternMatcher.java b/core/java/android/os/PatternMatcher.java
index b5425b4..79a2c59 100644
--- a/core/java/android/os/PatternMatcher.java
+++ b/core/java/android/os/PatternMatcher.java
@@ -16,9 +16,12 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 
 /**
@@ -68,6 +71,17 @@
      */
     public static final int PATTERN_SUFFIX = 4;
 
+    /** @hide */
+    @IntDef(value = {
+            PATTERN_LITERAL,
+            PATTERN_PREFIX,
+            PATTERN_SIMPLE_GLOB,
+            PATTERN_ADVANCED_GLOB,
+            PATTERN_SUFFIX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PatternType {}
+
     // token types for advanced matching
     private static final int TOKEN_TYPE_LITERAL = 0;
     private static final int TOKEN_TYPE_ANY = 1;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 533946d..d6df8d9 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1898,6 +1898,30 @@
             "no_near_field_communication_radio";
 
     /**
+     * This user restriction specifies if Thread network is disallowed on the device. If Thread
+     * network is disallowed it cannot be turned on via Settings.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In both cases, the restriction applies globally on the device and will turn off the
+     * Thread network radio if it's currently on and prevent the radio from being turned
+     * on in the future.
+     *
+     * <p> <a href="https://www.threadgroup.org">Thread</a> is a low-power and low-latency wireless
+     * mesh networking protocol built on IPv6.
+     *
+     * <p>Default is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled")
+    public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
+
+    /**
      * List of key values that can be passed into the various user restriction related methods
      * in {@link UserManager} & {@link DevicePolicyManager}.
      * Note: This is slightly different from the real set of user restrictions listed in {@link
@@ -1983,6 +2007,7 @@
             DISALLOW_ULTRA_WIDEBAND_RADIO,
             DISALLOW_GRANT_ADMIN,
             DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
+            DISALLOW_THREAD_NETWORK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserRestrictionKey {}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 5ad2502..298bdb8 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -622,6 +622,15 @@
                     new FillCallback(callback, request.getId())));
         }
 
+        @Override
+        public void onConvertCredentialRequest(
+                @NonNull ConvertCredentialRequest convertCredentialRequest,
+                @NonNull IConvertCredentialCallback convertCredentialCallback) {
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onConvertCredentialRequest,
+                    AutofillService.this, convertCredentialRequest,
+                    new ConvertCredentialCallback(convertCredentialCallback)));
+        }
 
         @Override
         public void onFillCredentialRequest(FillRequest request, IFillCallback callback,
@@ -707,7 +716,19 @@
      */
     public void onFillCredentialRequest(@NonNull FillRequest request,
             @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback,
-            IAutoFillManagerClient autofillClientCallback) {}
+            @NonNull IAutoFillManagerClient autofillClientCallback) {}
+
+    /**
+     * Called by the Android system to convert a credential manager response to a dataset
+     *
+     * @param convertCredentialRequest the request that has the original credential manager response
+     * @param convertCredentialCallback callback used to notify the result of the request.
+     *
+     * @hide
+     */
+    public void onConvertCredentialRequest(
+            @NonNull ConvertCredentialRequest convertCredentialRequest,
+            @NonNull ConvertCredentialCallback convertCredentialCallback){}
 
     /**
      * Called when the user requests the service to save the contents of a screen.
diff --git a/core/java/android/service/autofill/ConvertCredentialCallback.java b/core/java/android/service/autofill/ConvertCredentialCallback.java
new file mode 100644
index 0000000..a39f011
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialCallback.java
@@ -0,0 +1,65 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+
+/**
+ * <p><code>ConvertCredentialCallback</code> handles convertCredentialResponse from Autofill
+ * Service.
+ *
+ * @hide
+ */
+public final class ConvertCredentialCallback {
+
+    private static final String TAG = "ConvertCredentialCallback";
+
+    private final IConvertCredentialCallback mCallback;
+
+    /** @hide */
+    public ConvertCredentialCallback(IConvertCredentialCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Notifies the Android System that a convertCredentialRequest was fulfilled by the service.
+     *
+     * @param convertCredentialResponse the result
+     */
+    public void onSuccess(@NonNull ConvertCredentialResponse convertCredentialResponse) {
+        try {
+            mCallback.onSuccess(convertCredentialResponse);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notifies the Android System that a convert credential request has failed
+     *
+     * @param message the error message
+     */
+    public void onFailure(@Nullable CharSequence message) {
+        try {
+            mCallback.onFailure(message);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/autofill/ConvertCredentialRequest.aidl b/core/java/android/service/autofill/ConvertCredentialRequest.aidl
new file mode 100644
index 0000000..79681e2
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialRequest.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.autofill;
+
+parcelable ConvertCredentialRequest;
\ No newline at end of file
diff --git a/core/java/android/service/autofill/ConvertCredentialRequest.java b/core/java/android/service/autofill/ConvertCredentialRequest.java
new file mode 100644
index 0000000..d2d7556
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialRequest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.credentials.GetCredentialResponse;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * This class represents a request to an autofill service to convert the credential manager response
+ * to a dataset.
+ *
+ * @hide
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstructor = true,
+        genHiddenConstDefs = true)
+public final class ConvertCredentialRequest implements Parcelable {
+    private final @NonNull GetCredentialResponse mGetCredentialResponse;
+    private final @NonNull Bundle mClientState;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/ConvertCredentialRequest.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ConvertCredentialRequest.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public ConvertCredentialRequest(
+            @NonNull GetCredentialResponse getCredentialResponse,
+            @NonNull Bundle clientState) {
+        this.mGetCredentialResponse = getCredentialResponse;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mGetCredentialResponse);
+        this.mClientState = clientState;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mClientState);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull GetCredentialResponse getGetCredentialResponse() {
+        return mGetCredentialResponse;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Bundle getClientState() {
+        return mClientState;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ConvertCredentialRequest { " +
+                "getCredentialResponse = " + mGetCredentialResponse + ", " +
+                "clientState = " + mClientState +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeTypedObject(mGetCredentialResponse, flags);
+        dest.writeBundle(mClientState);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ConvertCredentialRequest(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        GetCredentialResponse getCredentialResponse = (GetCredentialResponse) in.readTypedObject(GetCredentialResponse.CREATOR);
+        Bundle clientState = in.readBundle();
+
+        this.mGetCredentialResponse = getCredentialResponse;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mGetCredentialResponse);
+        this.mClientState = clientState;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mClientState);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ConvertCredentialRequest> CREATOR
+            = new Parcelable.Creator<ConvertCredentialRequest>() {
+        @Override
+        public ConvertCredentialRequest[] newArray(int size) {
+            return new ConvertCredentialRequest[size];
+        }
+
+        @Override
+        public ConvertCredentialRequest createFromParcel(@NonNull Parcel in) {
+            return new ConvertCredentialRequest(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1706132305002L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/ConvertCredentialRequest.java",
+            inputSignatures = "private final @android.annotation.NonNull android.credentials.GetCredentialResponse mGetCredentialResponse\nprivate final @android.annotation.NonNull android.os.Bundle mClientState\nclass ConvertCredentialRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/ConvertCredentialResponse.aidl b/core/java/android/service/autofill/ConvertCredentialResponse.aidl
new file mode 100644
index 0000000..98ac6f6
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialResponse.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.autofill;
+
+parcelable ConvertCredentialResponse;
\ No newline at end of file
diff --git a/core/java/android/service/autofill/ConvertCredentialResponse.java b/core/java/android/service/autofill/ConvertCredentialResponse.java
new file mode 100644
index 0000000..5da4f63
--- /dev/null
+++ b/core/java/android/service/autofill/ConvertCredentialResponse.java
@@ -0,0 +1,157 @@
+/*
+ * 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.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Response for a {@Link ConvertCredentialRequest}
+ *
+ * @hide
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstructor = true,
+        genHiddenConstDefs = true)
+public final class ConvertCredentialResponse implements Parcelable {
+    private final @NonNull Dataset mDataset;
+    private final @Nullable Bundle mClientState;
+
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/ConvertCredentialResponse.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new ConvertCredentialResponse.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public ConvertCredentialResponse(
+            @NonNull Dataset dataset,
+            @Nullable Bundle clientState) {
+        this.mDataset = dataset;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDataset);
+        this.mClientState = clientState;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Dataset getDataset() {
+        return mDataset;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Bundle getClientState() {
+        return mClientState;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ConvertCredentialResponse { " +
+                "dataset = " + mDataset + ", " +
+                "clientState = " + mClientState +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mClientState != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeTypedObject(mDataset, flags);
+        if (mClientState != null) dest.writeBundle(mClientState);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ConvertCredentialResponse(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        Dataset dataset = (Dataset) in.readTypedObject(Dataset.CREATOR);
+        Bundle clientState = (flg & 0x2) == 0 ? null : in.readBundle();
+
+        this.mDataset = dataset;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDataset);
+        this.mClientState = clientState;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ConvertCredentialResponse> CREATOR
+            = new Parcelable.Creator<ConvertCredentialResponse>() {
+        @Override
+        public ConvertCredentialResponse[] newArray(int size) {
+            return new ConvertCredentialResponse[size];
+        }
+
+        @Override
+        public ConvertCredentialResponse createFromParcel(@NonNull Parcel in) {
+            return new ConvertCredentialResponse(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1706132669373L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/ConvertCredentialResponse.java",
+            inputSignatures = "private final @android.annotation.NonNull android.service.autofill.Dataset mDataset\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nclass ConvertCredentialResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 5d58120..98dda10 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -309,12 +309,19 @@
         /** The autofill suggestion is shown as a dialog presentation. */
         public static final int UI_TYPE_DIALOG = 3;
 
+        /**
+         *  The autofill suggestion is shown os a credman bottom sheet
+         *  @hide
+         */
+        public static final int UI_TYPE_CREDMAN_BOTTOM_SHEET = 4;
+
         /** @hide */
         @IntDef(prefix = { "UI_TYPE_" }, value = {
                 UI_TYPE_UNKNOWN,
                 UI_TYPE_MENU,
                 UI_TYPE_INLINE,
-                UI_TYPE_DIALOG
+                UI_TYPE_DIALOG,
+                UI_TYPE_CREDMAN_BOTTOM_SHEET
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface UiType {}
@@ -755,6 +762,8 @@
                     return "UI_TYPE_INLINE";
                 case UI_TYPE_DIALOG:
                     return "UI_TYPE_FILL_DIALOG";
+                case UI_TYPE_CREDMAN_BOTTOM_SHEET:
+                    return "UI_TYPE_CREDMAN_BOTTOM_SHEET";
                 default:
                     return "UI_TYPE_UNKNOWN";
             }
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 03ead32..2c2feae 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,6 +16,8 @@
 
 package android.service.autofill;
 
+import android.service.autofill.ConvertCredentialRequest;
+import android.service.autofill.IConvertCredentialCallback;
 import android.service.autofill.FillRequest;
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
@@ -32,7 +34,8 @@
     void onConnectedStateChanged(boolean connected);
     void onFillRequest(in FillRequest request, in IFillCallback callback);
     void onFillCredentialRequest(in FillRequest request, in IFillCallback callback,
-    in IAutoFillManagerClient client);
+        in IAutoFillManagerClient client);
     void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
     void onSavedPasswordCountRequest(in IResultReceiver receiver);
+    void onConvertCredentialRequest(in ConvertCredentialRequest convertCredentialRequest, in IConvertCredentialCallback convertCredentialCallback);
 }
diff --git a/core/java/android/service/autofill/IConvertCredentialCallback.aidl b/core/java/android/service/autofill/IConvertCredentialCallback.aidl
new file mode 100644
index 0000000..9dfc294
--- /dev/null
+++ b/core/java/android/service/autofill/IConvertCredentialCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.autofill;
+
+import android.os.ICancellationSignal;
+
+import android.service.autofill.ConvertCredentialResponse;
+
+/**
+ * Interface to receive the result of a convert credential request
+ *
+ * @hide
+ */
+oneway interface IConvertCredentialCallback {
+    void onSuccess(in ConvertCredentialResponse convertCredentialResponse);
+    void onFailure(CharSequence message);
+}
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index f28574e..27c509a 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -197,9 +197,28 @@
      * Transfer the currently in progress touch gesture from the host to the requested
      * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
      * SurfaceControlViewHost was created with the current host's inputToken.
+     * <p>
+     * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL
+     * and does not receive any further input events for this gesture.
+     * <p>
+     * The transferred-to window receives an ACTION_DOWN event and then the remainder of the
+     * input events for this gesture. It does not receive any of the previous events of this gesture
+     * that the originating window received.
+     * <p>
+     * The "transferTouch" API only works for the current gesture. When a new gesture arrives,
+     * input dispatcher will do a new round of hit testing. So, if the "host" window is still the
+     * first thing that's being touched, then it will receive the new gesture again. It will
+     * again be up to the host to transfer this new gesture to the embedded.
+     * <p>
+     * Once the transferred-to window receives the gesture, it can choose to give up this gesture
+     * and send it to another window that it's linked to (it can't be an arbitrary window for
+     * security reasons) using the same transferTouch API. Only the window currently receiving
+     * touch is allowed to transfer the gesture.
      *
      * @param surfacePackage The SurfacePackage to transfer the gesture to.
      * @return Whether the touch stream was transferred.
+     * @see SurfaceControlViewHost#transferTouchGestureToHost() for the reverse to transfer touch
+     * gesture from the embedded to the host.
      */
     @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED)
     default boolean transferHostTouchGestureToEmbedded(
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ba7874e..a1f44e4 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -1256,13 +1256,13 @@
     }
 
     private static void registerNativeMemoryUsage() {
-        if (Flags.enableSurfaceNativeAllocRegistration()) {
+        if (Flags.enableSurfaceNativeAllocRegistrationRo()) {
             VMRuntime.getRuntime().registerNativeAllocation(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
         }
     }
 
     private static void freeNativeMemoryUsage() {
-        if (Flags.enableSurfaceNativeAllocRegistration()) {
+        if (Flags.enableSurfaceNativeAllocRegistrationRo()) {
             VMRuntime.getRuntime().registerNativeFree(SURFACE_NATIVE_ALLOCATION_SIZE_BYTES);
         }
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 42355bb..427d053 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6022,8 +6022,8 @@
      * This is different from
      * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
      * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must
-     * invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources when
-     * no longer needing to use the {@link SurfaceControlInputReceiver}
+     * invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the
+     * resources when no longer needing to use the {@link SurfaceControlInputReceiver}
      *
      * @param displayId      The display that the SurfaceControl will be placed on. Input will
      *                       only work
@@ -6035,14 +6035,9 @@
      * @param choreographer  The Choreographer used for batching. This should match the rendering
      *                       Choreographer.
      * @param receiver       The SurfaceControlInputReceiver that will receive the input events
-     * @return an {@link IBinder} token that is used to unregister the input receiver via
-     * {@link #unregisterSurfaceControlInputReceiver(IBinder)}.
-     * @see #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
-     * SurfaceControlInputReceiver)
      */
     @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
-    @NonNull
-    default IBinder registerBatchedSurfaceControlInputReceiver(int displayId,
+    default void registerBatchedSurfaceControlInputReceiver(int displayId,
             @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
         throw new UnsupportedOperationException(
@@ -6054,8 +6049,8 @@
      * receive every input event. This is different than calling @link
      * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
      * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller
-     * must invoke {@link #unregisterSurfaceControlInputReceiver(IBinder)} to clean up the resources
-     * when no longer needing to use the {@link SurfaceControlInputReceiver}
+     * must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the
+     * resources when no longer needing to use the {@link SurfaceControlInputReceiver}
      *
      * @param displayId      The display that the SurfaceControl will be placed on. Input will only
      *                       work if SurfaceControl is on that display and that display was
@@ -6066,14 +6061,9 @@
      * @param surfaceControl The SurfaceControl to register the InputChannel for
      * @param looper         The looper to use when invoking callbacks.
      * @param receiver       The SurfaceControlInputReceiver that will receive the input events
-     * @return an {@link IBinder} token that is used to unregister the input receiver via
-     * {@link #unregisterSurfaceControlInputReceiver(IBinder)}.
-     * @see #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
-     * SurfaceControlInputReceiver)
      **/
     @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
-    @NonNull
-    default IBinder registerUnbatchedSurfaceControlInputReceiver(int displayId,
+    default void registerUnbatchedSurfaceControlInputReceiver(int displayId,
             @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
         throw new UnsupportedOperationException(
@@ -6091,17 +6081,32 @@
      * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
      * SurfaceControlInputReceiver)}
      *
-     * @param token The token that was returned via
-     *              {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder,
-     *              SurfaceControl,
-     *              Choreographer, SurfaceControlInputReceiver)} or
-     *              {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder,
-     *              SurfaceControl,
-     *              Looper, SurfaceControlInputReceiver)}
+     * @param surfaceControl The SurfaceControl to remove and unregister the input channel for.
      */
     @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
-    default void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) {
+    default void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
         throw new UnsupportedOperationException(
                 "unregisterSurfaceControlInputReceiver is not implemented");
     }
+
+    /**
+     * Returns the input client token for the {@link SurfaceControl}. This will only return non null
+     * if the SurfaceControl was registered for input via
+     * { #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
+     * SurfaceControlInputReceiver)} or
+     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
+     * SurfaceControlInputReceiver)}.
+     * <p>
+     * This is helpful for testing to ensure the test waits for the layer to be registered with
+     * SurfaceFlinger and Input before proceeding with the test.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+    @TestApi
+    @Nullable
+    default IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
+        throw new UnsupportedOperationException(
+                "getSurfaceControlInputClientToken is not implemented");
+    }
 }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8d40f9a..c49fce5 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -38,10 +38,12 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.inputmethod.InputMethodManager;
 import android.window.ITrustedPresentationListener;
 import android.window.TrustedPresentationThresholds;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
@@ -50,7 +52,6 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.function.IntConsumer;
@@ -156,8 +157,9 @@
     private final TrustedPresentationListener mTrustedPresentationListener =
             new TrustedPresentationListener();
 
-    private final ConcurrentHashMap<IBinder, InputEventReceiver> mSurfaceControlInputReceivers =
-            new ConcurrentHashMap<>();
+    @GuardedBy("mSurfaceControlInputReceivers")
+    private final SparseArray<SurfaceControlInputReceiverInfo>
+            mSurfaceControlInputReceivers = new SparseArray<>();
 
     private WindowManagerGlobal() {
     }
@@ -816,7 +818,7 @@
         mTrustedPresentationListener.removeListener(listener);
     }
 
-    IBinder registerBatchedSurfaceControlInputReceiver(int displayId,
+    void registerBatchedSurfaceControlInputReceiver(int displayId,
             @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
         IBinder clientToken = new Binder();
@@ -830,19 +832,21 @@
             e.rethrowAsRuntimeException();
         }
 
-        mSurfaceControlInputReceivers.put(clientToken,
-                new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(),
-                        choreographer) {
-                    @Override
-                    public void onInputEvent(InputEvent event) {
-                        boolean handled = receiver.onInputEvent(event);
-                        finishInputEvent(event, handled);
-                    }
-                });
-        return clientToken;
+        synchronized (mSurfaceControlInputReceivers) {
+            mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(),
+                    new SurfaceControlInputReceiverInfo(clientToken,
+                            new BatchedInputEventReceiver(inputChannel, choreographer.getLooper(),
+                                    choreographer) {
+                                @Override
+                                public void onInputEvent(InputEvent event) {
+                                    boolean handled = receiver.onInputEvent(event);
+                                    finishInputEvent(event, handled);
+                                }
+                            }));
+        }
     }
 
-    IBinder registerUnbatchedSurfaceControlInputReceiver(
+    void registerUnbatchedSurfaceControlInputReceiver(
             int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
         IBinder clientToken = new Binder();
@@ -856,32 +860,53 @@
             e.rethrowAsRuntimeException();
         }
 
-        mSurfaceControlInputReceivers.put(clientToken,
-                new InputEventReceiver(inputChannel, looper) {
-                    @Override
-                    public void onInputEvent(InputEvent event) {
-                        boolean handled = receiver.onInputEvent(event);
-                        finishInputEvent(event, handled);
-                    }
-                });
-
-        return clientToken;
+        synchronized (mSurfaceControlInputReceivers) {
+            mSurfaceControlInputReceivers.put(surfaceControl.getLayerId(),
+                    new SurfaceControlInputReceiverInfo(clientToken,
+                            new InputEventReceiver(inputChannel, looper) {
+                                @Override
+                                public void onInputEvent(InputEvent event) {
+                                    boolean handled = receiver.onInputEvent(event);
+                                    finishInputEvent(event, handled);
+                                }
+                            }));
+        }
     }
 
-    void unregisterSurfaceControlInputReceiver(IBinder token) {
-        InputEventReceiver inputEventReceiver = mSurfaceControlInputReceivers.get(token);
-        if (inputEventReceiver == null) {
-            Log.w(TAG, "No registered input event receiver with token: " + token);
+    void unregisterSurfaceControlInputReceiver(SurfaceControl surfaceControl) {
+        SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
+        synchronized (mSurfaceControlInputReceivers) {
+            surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.removeReturnOld(
+                    surfaceControl.getLayerId());
+        }
+
+        if (surfaceControlInputReceiverInfo == null) {
+            Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl);
             return;
         }
         try {
-            WindowManagerGlobal.getWindowSession().remove(token);
+            WindowManagerGlobal.getWindowSession().remove(
+                    surfaceControlInputReceiverInfo.mClientToken);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to remove input channel", e);
             e.rethrowAsRuntimeException();
         }
 
-        inputEventReceiver.dispose();
+        surfaceControlInputReceiverInfo.mInputEventReceiver.dispose();
+    }
+
+    IBinder getSurfaceControlInputClientToken(SurfaceControl surfaceControl) {
+        SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
+        synchronized (mSurfaceControlInputReceivers) {
+            surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.get(
+                    surfaceControl.getLayerId());
+        }
+
+        if (surfaceControlInputReceiverInfo == null) {
+            Log.w(TAG, "No registered input event receiver with sc: " + surfaceControl);
+            return null;
+        }
+        return surfaceControlInputReceiverInfo.mClientToken;
     }
 
     private final class TrustedPresentationListener extends
@@ -976,6 +1001,17 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private static class SurfaceControlInputReceiverInfo {
+        final IBinder mClientToken;
+        final InputEventReceiver mInputEventReceiver;
+
+        private SurfaceControlInputReceiverInfo(IBinder clientToken,
+                InputEventReceiver inputEventReceiver) {
+            mClientToken = clientToken;
+            mInputEventReceiver = inputEventReceiver;
+        }
+    }
 }
 
 final class WindowLeaked extends AndroidRuntimeException {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index aaf5fcc..41d181c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -523,26 +523,30 @@
         mGlobal.unregisterTrustedPresentationListener(listener);
     }
 
-    @NonNull
     @Override
-    public IBinder registerBatchedSurfaceControlInputReceiver(int displayId,
+    public void registerBatchedSurfaceControlInputReceiver(int displayId,
             @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
-        return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostToken,
+        mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostToken,
                 surfaceControl, choreographer, receiver);
     }
 
-    @NonNull
     @Override
-    public IBinder registerUnbatchedSurfaceControlInputReceiver(
+    public void registerUnbatchedSurfaceControlInputReceiver(
             int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
             @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
-        return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, hostToken,
+        mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId, hostToken,
                 surfaceControl, looper, receiver);
     }
 
     @Override
-    public void unregisterSurfaceControlInputReceiver(@NonNull IBinder token) {
-        mGlobal.unregisterSurfaceControlInputReceiver(token);
+    public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
+        mGlobal.unregisterSurfaceControlInputReceiver(surfaceControl);
+    }
+
+    @Override
+    @Nullable
+    public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
+        return mGlobal.getSurfaceControlInputClientToken(surfaceControl);
     }
 }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dbeffc8..559ccfea7 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -298,6 +298,15 @@
             "android.view.autofill.extra.AUGMENTED_AUTOFILL_CLIENT";
 
     /**
+     * Internal extra used to pass the fill request id in client state of
+     * {@link ConvertCredentialResponse}
+     *
+     * @hide
+     */
+    public static final String EXTRA_AUTOFILL_REQUEST_ID =
+            "android.view.autofill.extra.AUTOFILL_REQUEST_ID";
+
+    /**
      * Autofill Hint to indicate that it can match any field.
      *
      * @hide
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index befb002..5446428 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -30,6 +30,8 @@
 import android.util.Log;
 import android.view.SurfaceControl;
 
+import com.android.window.flags.Flags;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.util.concurrent.CountDownLatch;
@@ -48,7 +50,7 @@
     private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
             long captureListener);
     private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
-            long captureListener);
+            long captureListener, boolean sync);
     private static native long nativeCreateScreenCaptureListener(
             ObjIntConsumer<ScreenshotHardwareBuffer> consumer);
     private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
@@ -134,7 +136,8 @@
      */
     public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
         SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener();
-        int status = captureLayers(captureArgs, syncScreenCapture);
+        int status = nativeCaptureLayers(captureArgs, syncScreenCapture.mNativeObject,
+                Flags.syncScreenCapture());
         if (status != 0) {
             return null;
         }
@@ -171,7 +174,7 @@
      */
     public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
             @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
+        return nativeCaptureLayers(captureArgs, captureListener.mNativeObject, false /* sync */);
     }
 
     /**
@@ -674,7 +677,7 @@
      * This listener can only be used for a single call to capture content call.
      */
     public static class ScreenCaptureListener implements Parcelable {
-        private final long mNativeObject;
+        final long mNativeObject;
         private static final NativeAllocationRegistry sRegistry =
                 NativeAllocationRegistry.createMalloced(
                         ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 86804c6..65075ae 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -226,9 +226,6 @@
             setTopOnBackInvokedCallback(null);
         }
 
-        // We should also stop running animations since all callbacks have been removed.
-        // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler.
-        Handler.getMain().post(mProgressAnimator::reset);
         mAllCallbacks.clear();
         mOnBackInvokedCallbacks.clear();
     }
@@ -442,8 +439,7 @@
 
         return WindowOnBackInvokedDispatcher
                 .isOnBackInvokedCallbackEnabled(activityInfo, applicationInfo,
-                        () -> originalContext.obtainStyledAttributes(
-                                new int[] {android.R.attr.windowSwipeToDismiss}), true);
+                        () -> originalContext);
     }
 
     @Override
@@ -501,7 +497,7 @@
      */
     public static boolean isOnBackInvokedCallbackEnabled(@Nullable ActivityInfo activityInfo,
             @NonNull ApplicationInfo applicationInfo,
-            @NonNull Supplier<TypedArray> windowAttrSupplier, boolean recycleTypedArray) {
+            @NonNull Supplier<Context> contextSupplier) {
         // new back is enabled if the feature flag is enabled AND the app does not explicitly
         // request legacy back.
         if (!ENABLE_PREDICTIVE_BACK) {
@@ -547,15 +543,15 @@
             //    setTrigger(true)
             // Use the original context to resolve the styled attribute so that they stay
             // true to the window.
-            TypedArray windowAttr = windowAttrSupplier.get();
+            final Context context = contextSupplier.get();
             boolean windowSwipeToDismiss = true;
-            if (windowAttr != null) {
-                if (windowAttr.getIndexCount() > 0) {
-                    windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+            if (context != null) {
+                final TypedArray array = context.obtainStyledAttributes(
+                            new int[]{android.R.attr.windowSwipeToDismiss});
+                if (array.getIndexCount() > 0) {
+                    windowSwipeToDismiss = array.getBoolean(0, true);
                 }
-                if (recycleTypedArray) {
-                    windowAttr.recycle();
-                }
+                array.recycle();
             }
 
             if (DEBUG) {
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 751c1a8..069affb 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -88,3 +88,11 @@
     is_fixed_read_only: true
     bug: "304574518"
 }
+
+flag {
+    namespace: "window_surfaces"
+    name: "sync_screen_capture"
+    description: "Create a screen capture API that blocks in SurfaceFlinger"
+    is_fixed_read_only: true
+    bug: "321263247"
+}
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
index c6683cf..05728ee 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -18,9 +18,13 @@
 
 import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.UriRelativeFilter;
+import android.content.UriRelativeFilterGroup;
+import android.content.pm.Flags;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.res.Resources;
@@ -132,6 +136,11 @@
                 case "data":
                     result = parseData(intentInfo, res, parser, allowGlobs, input);
                     break;
+                case "uri-relative-filter-group":
+                    if (Flags.relativeReferenceIntentFilters()) {
+                        result = parseRelRefGroup(intentInfo, pkg, res, parser, allowGlobs, input);
+                        break;
+                    }
                 default:
                     result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
                     break;
@@ -163,6 +172,197 @@
     }
 
     @NonNull
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    private static ParseResult<ParsedIntentInfo> parseRelRefGroup(ParsedIntentInfo intentInfo,
+            ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
+            ParseInput input) throws XmlPullParserException, IOException {
+        IntentFilter intentFilter = intentInfo.getIntentFilter();
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestUriRelativeFilterGroup);
+        UriRelativeFilterGroup group;
+        try {
+            int action = UriRelativeFilterGroup.ACTION_ALLOW;
+            if (!sa.getBoolean(R.styleable.AndroidManifestUriRelativeFilterGroup_allow, true)) {
+                action = UriRelativeFilterGroup.ACTION_BLOCK;
+            }
+            group = new UriRelativeFilterGroup(action);
+        } finally {
+            sa.recycle();
+        }
+        final int depth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final ParseResult result;
+            String nodeName = parser.getName();
+            switch (nodeName) {
+                case "data":
+                    result = parseRelRefGroupData(group, res, parser, allowGlobs, input);
+                    break;
+                default:
+                    result = ParsingUtils.unknownTag("<uri-relative-filter-group>",
+                            pkg, parser, input);
+                    break;
+            }
+
+            if (result.isError()) {
+                return input.error(result);
+            }
+        }
+
+        if (group.getUriRelativeFilters().size() > 0) {
+            intentFilter.addUriRelativeFilterGroup(group);
+        }
+        return input.success(null);
+    }
+
+    @NonNull
+    @FlaggedApi(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    private static ParseResult<ParsedIntentInfo> parseRelRefGroupData(UriRelativeFilterGroup group,
+            Resources res, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestData);
+        try {
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_path, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH,
+                        PatternMatcher.PATTERN_LITERAL, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPrefix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH,
+                        PatternMatcher.PATTERN_PREFIX, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathPattern not allowed here; path must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH,
+                        PatternMatcher.PATTERN_SIMPLE_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "pathAdvancedPattern not allowed here; path must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH,
+                        PatternMatcher.PATTERN_ADVANCED_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_pathSuffix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.PATH,
+                        PatternMatcher.PATTERN_SUFFIX, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_fragment, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT,
+                        PatternMatcher.PATTERN_LITERAL, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_fragmentPrefix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT,
+                        PatternMatcher.PATTERN_PREFIX, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_fragmentPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "fragmentPattern not allowed here; fragment must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT,
+                        PatternMatcher.PATTERN_SIMPLE_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_fragmentAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "fragmentAdvancedPattern not allowed here; fragment must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT,
+                        PatternMatcher.PATTERN_ADVANCED_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_fragmentSuffix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.FRAGMENT,
+                        PatternMatcher.PATTERN_SUFFIX, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_query, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY,
+                        PatternMatcher.PATTERN_LITERAL, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_queryPrefix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY,
+                        PatternMatcher.PATTERN_PREFIX, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_queryPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "queryPattern not allowed here; query must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY,
+                        PatternMatcher.PATTERN_SIMPLE_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_queryAdvancedPattern, 0);
+            if (str != null) {
+                if (!allowGlobs) {
+                    return input.error(
+                            "queryAdvancedPattern not allowed here; query must be literal");
+                }
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY,
+                        PatternMatcher.PATTERN_ADVANCED_GLOB, str));
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestData_querySuffix, 0);
+            if (str != null) {
+                group.addUriRelativeFilter(new UriRelativeFilter(UriRelativeFilter.QUERY,
+                        PatternMatcher.PATTERN_SUFFIX, str));
+            }
+
+            return input.success(null);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    @NonNull
     private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
             Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
         IntentFilter intentFilter = intentInfo.getIntentFilter();
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 6e903b3..1031542 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -211,7 +211,7 @@
 }
 
 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
-                                jlong screenCaptureListenerObject) {
+                                jlong screenCaptureListenerObject, jboolean sync) {
     LayerCaptureArgs captureArgs;
     getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
 
@@ -227,7 +227,7 @@
 
     sp<gui::IScreenCaptureListener> captureListener =
             reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
-    return ScreenshotClient::captureLayers(captureArgs, captureListener);
+    return ScreenshotClient::captureLayers(captureArgs, captureListener, sync);
 }
 
 static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
@@ -281,7 +281,7 @@
         // clang-format off
     {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
             (void*)nativeCaptureDisplay },
-    {"nativeCaptureLayers",  "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
+    {"nativeCaptureLayers",  "(Landroid/window/ScreenCapture$LayerCaptureArgs;JZ)I",
             (void*)nativeCaptureLayers },
     {"nativeCreateScreenCaptureListener", "(Ljava/util/function/ObjIntConsumer;)J",
             (void*)nativeCreateScreenCaptureListener },
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 75e2908..1d1f88b 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -66,7 +66,7 @@
     optional string identifier = 13 [ (.android.privacy).dest = DEST_EXPLICIT ];
 }
 
-// Next Tag: 12
+// Next Tag: 14
 message IntentFilterProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -89,6 +89,7 @@
     optional bool get_auto_verify = 10;
     repeated string mime_groups = 11;
     optional android.os.PersistableBundleProto extras = 12;
+    repeated UriRelativeFilterGroupProto uri_relative_filter_groups = 13;
 }
 
 message AuthorityEntryProto {
@@ -98,3 +99,23 @@
     optional bool wild = 2;
     optional int32 port = 3;
 }
+
+message UriRelativeFilterGroupProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    enum Action {
+        ACTION_ALLOW = 0;
+        ACTION_BLOCK = 1;
+    }
+
+    optional Action action = 1;
+    repeated UriRelativeFilterProto uri_relative_filters = 2;
+}
+
+message UriRelativeFilterProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    required int32 uri_part = 1;
+    required int32 pattern_type = 2;
+    required string filter = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0e0af4d..d972556 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7985,6 +7985,16 @@
     <permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"
                 android:protectionLevel="signature|privileged"/>
 
+    <!-- @SystemApi
+        @FlaggedApi("android.content.pm.emergency_install_permission")
+        Allows each app store in the system image to designate another app in the system image to
+        update the app store
+        <p>Protection level: signature|privileged
+        @hide
+    -->
+    <permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES"
+                android:protectionLevel="signature|privileged"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 35276bf..6884fc0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3438,6 +3438,20 @@
     <!-- Attributes that can be supplied in an AndroidManifest.xml
          <code>data</code> tag, a child of the
          {@link #AndroidManifestIntentFilter intent-filter} tag, describing
+         a group matching rule consisting of one or more
+         {@link #AndroidManifestData data} tags that must all match.  This
+         tag can be specified multiple times to create multiple groups that
+         will be matched in the order they are defined. -->
+    <declare-styleable name="AndroidManifestUriRelativeFilterGroup"
+        parent="AndroidManifestIntentFilter">
+        <!-- Specify if this group is allow rule or disallow rule.  If this
+             attribute is not specified then it is assumed to be true -->
+        <attr name="allow" format="boolean"/>
+    </declare-styleable>
+
+    <!-- Attributes that can be supplied in an AndroidManifest.xml
+         <code>data</code> tag, a child of the
+         {@link #AndroidManifestIntentFilter intent-filter} tag, describing
          the types of data that match.  This tag can be specified multiple
          times to supply multiple data options, as described in the
          {@link android.content.IntentFilter} class.  Note that all such
@@ -3445,7 +3459,8 @@
          <code>&lt;data android:scheme="myscheme" android:host="me.com" /&gt;</code>
          is equivalent to <code>&lt;data android:scheme="myscheme" /&gt;
          &lt;data android:host="me.com" /&gt;</code>. -->
-    <declare-styleable name="AndroidManifestData" parent="AndroidManifestIntentFilter">
+    <declare-styleable name="AndroidManifestData"
+        parent="AndroidManifestIntentFilter AndroidManifestUriRelativeFilterGroup">
         <!-- Specify a MIME type that is handled, as per
              {@link android.content.IntentFilter#addDataType
              IntentFilter.addDataType()}.
@@ -3549,6 +3564,70 @@
              IntentFilter.addDataPath()} with
              {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
         <attr name="pathSuffix" />
+        <!-- Specify a URI query that must exactly match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
+        <attr name="query" format="string" />
+        <!-- Specify a URI query that must be a prefix to match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
+        <attr name="queryPrefix" format="string" />
+        <!-- Specify a URI query that matches a simple pattern, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
+             Note that because '\' is used as an escape character when
+             reading the string from XML (before it is parsed as a pattern),
+             you will need to double-escape: for example a literal "*" would
+             be written as "\\*" and a literal "\" would be written as
+             "\\\\".  This is basically the same as what you would need to
+             write if constructing the string in Java code. -->
+        <attr name="queryPattern" format="string" />
+        <!-- Specify a URI query that matches an advanced pattern, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.
+             Note that because '\' is used as an escape character when
+             reading the string from XML (before it is parsed as a pattern),
+             you will need to double-escape: for example a literal "*" would
+             be written as "\\*" and a literal "\" would be written as
+             "\\\\".  This is basically the same as what you would need to
+             write if constructing the string in Java code. -->
+        <attr name="queryAdvancedPattern" format="string" />
+        <!-- Specify a URI query that must be a suffix to match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
+        <attr name="querySuffix" format="string" />
+        <!-- Specify a URI fragment that must exactly match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
+        <attr name="fragment" format="string" />
+        <!-- Specify a URI fragment that must be a prefix to match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
+        <attr name="fragmentPrefix" format="string" />
+        <!-- Specify a URI fragment that matches a simple pattern, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
+             Note that because '\' is used as an escape character when
+             reading the string from XML (before it is parsed as a pattern),
+             you will need to double-escape: for example a literal "*" would
+             be written as "\\*" and a literal "\" would be written as
+             "\\\\".  This is basically the same as what you would need to
+             write if constructing the string in Java code. -->
+        <attr name="fragmentPattern" format="string" />
+        <!-- Specify a URI fragment that matches an advanced pattern, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.
+             Note that because '\' is used as an escape character when
+             reading the string from XML (before it is parsed as a pattern),
+             you will need to double-escape: for example a literal "*" would
+             be written as "\\*" and a literal "\" would be written as
+             "\\\\".  This is basically the same as what you would need to
+             write if constructing the string in Java code. -->
+        <attr name="fragmentAdvancedPattern" format="string" />
+        <!-- Specify a URI fragment that must be a suffix to match, as a
+             {@link android.content.UriRelativeFilter UriRelativeFilter} with
+             {@link android.os.PatternMatcher#PATTERN_SUFFIX}. -->
+        <attr name="fragmentSuffix" format="string" />
     </declare-styleable>
 
     <!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index b8fc052..830e99c 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -123,6 +123,26 @@
     <public name="featureFlag"/>
     <!-- @FlaggedApi("android.multiuser.enable_system_user_only_for_services_and_providers") -->
     <public name="systemUserOnly"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="allow"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="query"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryPrefix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="queryAdvancedPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="querySuffix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentPrefix"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentAdvancedPattern"/>
+    <!-- @FlaggedApi("android.content.pm.relative_reference_intent_filters") -->
+    <public name="fragmentSuffix"/>
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01bc0000">
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 3a2e50a..9bb2499 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
     <!-- Arab Emirates -->
-    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
+    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
 
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -86,7 +86,7 @@
     <shortcode country="cn" premium="1066.*" free="1065.*" />
 
     <!-- Colombia: 1-6 digits (not confirmed) -->
-    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517" />
+    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" />
 
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
@@ -104,6 +104,12 @@
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
 
+    <!-- Dominican Republic: 1-6 digits (standard system default, not country specific) -->
+    <shortcode country="do" pattern="\\d{1,6}" free="912892" />
+
+    <!-- Ecuador: 1-6 digits (standard system default, not country specific) -->
+    <shortcode country="ec" pattern="\\d{1,6}" free="466453" />
+
     <!-- Estonia: short codes 3-5 digits starting with 1, plus premium 7 digit numbers starting with 90, plus EU.
          http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
     <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
@@ -154,8 +160,8 @@
          http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
     <shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
 
-    <!-- Israel: 4 digits, known premium codes listed -->
-    <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
+    <!-- Israel: 1-5 digits, known premium codes listed -->
+    <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477|6681" />
 
     <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
          https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
@@ -193,11 +199,14 @@
     <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
 
     <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453" />
+    <shortcode country="mx" pattern="\\d{4,6}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453|550346" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
 
+    <!-- Namibia: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="na" pattern="\\d{1,5}" free="40005" />
+
     <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
     <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
 
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index a709d7b..52ff0d4 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -20,6 +20,7 @@
 import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeast;
@@ -358,7 +359,7 @@
     }
 
     @Test
-    public void onDetachFromWindow_cancelCallbackAndIgnoreOnBackInvoked() throws RemoteException {
+    public void onDetachFromWindow_cancelsBackAnimation() throws RemoteException {
         mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
 
         OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo();
@@ -368,13 +369,12 @@
         waitForIdle();
         verify(mCallback1).onBackStarted(any(BackEvent.class));
 
-        // This should trigger mCallback1.onBackCancelled()
+        // This should trigger mCallback1.onBackCancelled() and unset the callback in WM
         mDispatcher.detachFromWindow();
-        // This should be ignored by mCallback1
-        callbackInfo.getCallback().onBackInvoked();
 
+        OnBackInvokedCallbackInfo callbackInfo1 = assertSetCallbackInfo();
+        assertNull(callbackInfo1);
         waitForIdle();
-        verify(mCallback1, never()).onBackInvoked();
         verify(mCallback1).onBackCancelled();
     }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 62c9e16..2873428 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -642,5 +642,6 @@
 
    <privapp-permissions package="com.android.devicediagnostics">
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.BATTERY_STATS"/>
     </privapp-permissions>
 </permissions>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 554b1fb..4ba05ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -32,6 +32,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
@@ -60,7 +61,6 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -544,12 +544,22 @@
             return true;
         }
 
+        /**
+         * Perform a task size toggle on release of the double-tap, assuming no drag event
+         * was handled during the double-tap.
+         * @param e The motion event that occurred during the double-tap gesture.
+         * @return true if the event should be consumed, false if not
+         */
         @Override
-        public boolean onDoubleTap(@NonNull MotionEvent e) {
+        public boolean onDoubleTapEvent(@NonNull MotionEvent e) {
+            final int action = e.getActionMasked();
+            if (mIsDragging || (action != MotionEvent.ACTION_UP
+                    && action != MotionEvent.ACTION_CANCEL)) {
+                return false;
+            }
             final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
-            mDesktopTasksController.ifPresent(c -> {
-                c.toggleDesktopTaskSize(taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId));
-            });
+            mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo,
+                    mWindowDecorByTaskId.get(taskInfo.taskId)));
             return true;
         }
     }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0abb6f5..4e330da 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -629,14 +629,6 @@
             // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
             cflags: ["-Wno-implicit-fallthrough"],
         },
-        host: {
-            srcs: [
-                "utils/HostColorSpace.cpp",
-            ],
-            export_static_lib_headers: [
-                "libarect",
-            ],
-        },
     },
 }
 
diff --git a/libs/hwui/utils/HostColorSpace.cpp b/libs/hwui/utils/HostColorSpace.cpp
deleted file mode 100644
index 77a6820..0000000
--- a/libs/hwui/utils/HostColorSpace.cpp
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-// This is copied from framework/native/libs/ui in order not to include libui in host build
-
-#include <ui/ColorSpace.h>
-
-using namespace std::placeholders;
-
-namespace android {
-
-static constexpr float linearResponse(float v) {
-    return v;
-}
-
-static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
-    return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
-}
-
-static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
-    return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
-}
-
-static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
-    return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
-}
-
-static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
-    return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
-}
-
-static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
-    float xx = std::abs(x);
-    return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
-}
-
-static float absResponse(float x, float g, float a, float b, float c, float d) {
-   float xx = std::abs(x);
-   return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
-}
-
-static float safePow(float x, float e) {
-    return powf(x < 0.0f ? 0.0f : x, e);
-}
-
-static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
-    if (parameters.e == 0.0f && parameters.f == 0.0f) {
-        return std::bind(rcpResponse, _1, parameters);
-    }
-    return std::bind(rcpFullResponse, _1, parameters);
-}
-
-static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
-    if (parameters.e == 0.0f && parameters.f == 0.0f) {
-        return std::bind(response, _1, parameters);
-    }
-    return std::bind(fullResponse, _1, parameters);
-}
-
-static ColorSpace::transfer_function toOETF(float gamma) {
-    if (gamma == 1.0f) {
-        return linearResponse;
-    }
-    return std::bind(safePow, _1, 1.0f / gamma);
-}
-
-static ColorSpace::transfer_function toEOTF(float gamma) {
-    if (gamma == 1.0f) {
-        return linearResponse;
-    }
-    return std::bind(safePow, _1, gamma);
-}
-
-static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
-    float3 r(rgbToXYZ * float3{1, 0, 0});
-    float3 g(rgbToXYZ * float3{0, 1, 0});
-    float3 b(rgbToXYZ * float3{0, 0, 1});
-
-    return {{r.xy / dot(r, float3{1}),
-             g.xy / dot(g, float3{1}),
-             b.xy / dot(b, float3{1})}};
-}
-
-static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
-    float3 w(rgbToXYZ * float3{1});
-    return w.xy / dot(w, float3{1});
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const mat3& rgbToXYZ,
-        transfer_function OETF,
-        transfer_function EOTF,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(rgbToXYZ)
-        , mXYZtoRGB(inverse(rgbToXYZ))
-        , mOETF(std::move(OETF))
-        , mEOTF(std::move(EOTF))
-        , mClamper(std::move(clamper))
-        , mPrimaries(computePrimaries(rgbToXYZ))
-        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const mat3& rgbToXYZ,
-        const TransferParameters parameters,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(rgbToXYZ)
-        , mXYZtoRGB(inverse(rgbToXYZ))
-        , mParameters(parameters)
-        , mOETF(toOETF(mParameters))
-        , mEOTF(toEOTF(mParameters))
-        , mClamper(std::move(clamper))
-        , mPrimaries(computePrimaries(rgbToXYZ))
-        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const mat3& rgbToXYZ,
-        float gamma,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(rgbToXYZ)
-        , mXYZtoRGB(inverse(rgbToXYZ))
-        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
-        , mOETF(toOETF(gamma))
-        , mEOTF(toEOTF(gamma))
-        , mClamper(std::move(clamper))
-        , mPrimaries(computePrimaries(rgbToXYZ))
-        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const std::array<float2, 3>& primaries,
-        const float2& whitePoint,
-        transfer_function OETF,
-        transfer_function EOTF,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
-        , mXYZtoRGB(inverse(mRGBtoXYZ))
-        , mOETF(std::move(OETF))
-        , mEOTF(std::move(EOTF))
-        , mClamper(std::move(clamper))
-        , mPrimaries(primaries)
-        , mWhitePoint(whitePoint) {
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const std::array<float2, 3>& primaries,
-        const float2& whitePoint,
-        const TransferParameters parameters,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
-        , mXYZtoRGB(inverse(mRGBtoXYZ))
-        , mParameters(parameters)
-        , mOETF(toOETF(mParameters))
-        , mEOTF(toEOTF(mParameters))
-        , mClamper(std::move(clamper))
-        , mPrimaries(primaries)
-        , mWhitePoint(whitePoint) {
-}
-
-ColorSpace::ColorSpace(
-        const std::string& name,
-        const std::array<float2, 3>& primaries,
-        const float2& whitePoint,
-        float gamma,
-        clamping_function clamper) noexcept
-        : mName(name)
-        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
-        , mXYZtoRGB(inverse(mRGBtoXYZ))
-        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
-        , mOETF(toOETF(gamma))
-        , mEOTF(toEOTF(gamma))
-        , mClamper(std::move(clamper))
-        , mPrimaries(primaries)
-        , mWhitePoint(whitePoint) {
-}
-
-constexpr mat3 ColorSpace::computeXYZMatrix(
-        const std::array<float2, 3>& primaries, const float2& whitePoint) {
-    const float2& R = primaries[0];
-    const float2& G = primaries[1];
-    const float2& B = primaries[2];
-    const float2& W = whitePoint;
-
-    float oneRxRy = (1 - R.x) / R.y;
-    float oneGxGy = (1 - G.x) / G.y;
-    float oneBxBy = (1 - B.x) / B.y;
-    float oneWxWy = (1 - W.x) / W.y;
-
-    float RxRy = R.x / R.y;
-    float GxGy = G.x / G.y;
-    float BxBy = B.x / B.y;
-    float WxWy = W.x / W.y;
-
-    float BY =
-            ((oneWxWy - oneRxRy) * (GxGy - RxRy) - (WxWy - RxRy) * (oneGxGy - oneRxRy)) /
-            ((oneBxBy - oneRxRy) * (GxGy - RxRy) - (BxBy - RxRy) * (oneGxGy - oneRxRy));
-    float GY = (WxWy - RxRy - BY * (BxBy - RxRy)) / (GxGy - RxRy);
-    float RY = 1 - GY - BY;
-
-    float RYRy = RY / R.y;
-    float GYGy = GY / G.y;
-    float BYBy = BY / B.y;
-
-    return {
-        float3{RYRy * R.x, RY, RYRy * (1 - R.x - R.y)},
-        float3{GYGy * G.x, GY, GYGy * (1 - G.x - G.y)},
-        float3{BYBy * B.x, BY, BYBy * (1 - B.x - B.y)}
-    };
-}
-
-const ColorSpace ColorSpace::sRGB() {
-    return {
-        "sRGB IEC61966-2.1",
-        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f},
-        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::linearSRGB() {
-    return {
-        "sRGB IEC61966-2.1 (Linear)",
-        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f}
-    };
-}
-
-const ColorSpace ColorSpace::extendedSRGB() {
-    return {
-        "scRGB-nl IEC 61966-2-2:2003",
-        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f},
-        std::bind(absRcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
-        std::bind(absResponse,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
-        std::bind(clamp<float>, _1, -0.799f, 2.399f)
-    };
-}
-
-const ColorSpace ColorSpace::linearExtendedSRGB() {
-    return {
-        "scRGB IEC 61966-2-2:2003",
-        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f},
-        1.0f,
-        std::bind(clamp<float>, _1, -0.5f, 7.499f)
-    };
-}
-
-const ColorSpace ColorSpace::NTSC() {
-    return {
-        "NTSC (1953)",
-        {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
-        {0.310f, 0.316f},
-        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::BT709() {
-    return {
-        "Rec. ITU-R BT.709-5",
-        {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f},
-        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::BT2020() {
-    return {
-        "Rec. ITU-R BT.2020-1",
-        {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
-        {0.3127f, 0.3290f},
-        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::AdobeRGB() {
-    return {
-        "Adobe RGB (1998)",
-        {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
-        {0.3127f, 0.3290f},
-        2.2f
-    };
-}
-
-const ColorSpace ColorSpace::ProPhotoRGB() {
-    return {
-        "ROMM RGB ISO 22028-2:2013",
-        {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
-        {0.34567f, 0.35850f},
-        {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::DisplayP3() {
-    return {
-        "Display P3",
-        {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
-        {0.3127f, 0.3290f},
-        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
-    };
-}
-
-const ColorSpace ColorSpace::DCIP3() {
-    return {
-        "SMPTE RP 431-2-2007 DCI (P3)",
-        {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
-        {0.314f, 0.351f},
-        2.6f
-    };
-}
-
-const ColorSpace ColorSpace::ACES() {
-    return {
-        "SMPTE ST 2065-1:2012 ACES",
-        {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
-        {0.32168f, 0.33767f},
-        1.0f,
-        std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
-    };
-}
-
-const ColorSpace ColorSpace::ACEScg() {
-    return {
-        "Academy S-2014-004 ACEScg",
-        {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
-        {0.32168f, 0.33767f},
-        1.0f,
-        std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
-    };
-}
-
-std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src,
-                                                const ColorSpace& dst) {
-    size = clamp(size, 2u, 256u);
-    float m = 1.0f / float(size - 1);
-
-    std::unique_ptr<float3[]> lut(new float3[size * size * size]);
-    float3* data = lut.get();
-
-    ColorSpaceConnector connector(src, dst);
-
-    for (uint32_t z = 0; z < size; z++) {
-        for (int32_t y = int32_t(size - 1); y >= 0; y--) {
-            for (uint32_t x = 0; x < size; x++) {
-                *data++ = connector.transform({x * m, y * m, z * m});
-            }
-        }
-    }
-
-    return lut;
-}
-
-static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
-static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
-static const mat3 BRADFORD = mat3{
-    float3{ 0.8951f, -0.7502f,  0.0389f},
-    float3{ 0.2664f,  1.7135f, -0.0685f},
-    float3{-0.1614f,  0.0367f,  1.0296f}
-};
-
-static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) {
-    float3 srcLMS = matrix * srcWhitePoint;
-    float3 dstLMS = matrix * dstWhitePoint;
-    return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
-}
-
-ColorSpaceConnector::ColorSpaceConnector(
-        const ColorSpace& src,
-        const ColorSpace& dst) noexcept
-        : mSource(src)
-        , mDestination(dst) {
-
-    if (all(lessThan(abs(src.getWhitePoint() - dst.getWhitePoint()), float2{1e-3f}))) {
-        mTransform = dst.getXYZtoRGB() * src.getRGBtoXYZ();
-    } else {
-        mat3 rgbToXYZ(src.getRGBtoXYZ());
-        mat3 xyzToRGB(dst.getXYZtoRGB());
-
-        float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
-        float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
-
-        if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
-            rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
-        }
-
-        if (any(greaterThan(abs(dst.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
-            xyzToRGB = inverse(adaptation(BRADFORD, dstXYZ, ILLUMINANT_D50_XYZ) * dst.getRGBtoXYZ());
-        }
-
-        mTransform = xyzToRGB * rgbToXYZ;
-    }
-}
-
-}; // namespace android
diff --git a/location/java/android/location/altitude/AltitudeConverter.java b/location/java/android/location/altitude/AltitudeConverter.java
index 3dc024ef..6f88912 100644
--- a/location/java/android/location/altitude/AltitudeConverter.java
+++ b/location/java/android/location/altitude/AltitudeConverter.java
@@ -19,9 +19,11 @@
 import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.content.Context;
+import android.frameworks.location.altitude.GetGeoidHeightRequest;
+import android.frameworks.location.altitude.GetGeoidHeightResponse;
 import android.location.Location;
 
-import com.android.internal.location.altitude.GeoidHeightMap;
+import com.android.internal.location.altitude.GeoidMap;
 import com.android.internal.location.altitude.S2CellIdUtils;
 import com.android.internal.location.altitude.nano.MapParamsProto;
 import com.android.internal.util.Preconditions;
@@ -37,7 +39,7 @@
  * <pre>
  * Brian Julian and Michael Angermann.
  * "Resource efficient and accurate altitude conversion to Mean Sea Level."
- * To appear in 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS).
+ * 2023 IEEE/ION Position, Location and Navigation Symposium (PLANS).
  * </pre>
  */
 public final class AltitudeConverter {
@@ -45,8 +47,8 @@
     private static final double MAX_ABS_VALID_LATITUDE = 90;
     private static final double MAX_ABS_VALID_LONGITUDE = 180;
 
-    /** Manages a mapping of geoid heights associated with S2 cells. */
-    private final GeoidHeightMap mGeoidHeightMap = new GeoidHeightMap();
+    /** Manages a mapping of geoid heights and expiration distances associated with S2 cells. */
+    private final GeoidMap mGeoidMap = new GeoidMap();
 
     /**
      * Creates an instance that manages an independent cache to optimized conversions of locations
@@ -78,75 +80,87 @@
     /**
      * Returns the four S2 cell IDs for the map square associated with the {@code location}.
      *
-     * <p>The first map cell contains the location, while the others are located horizontally,
-     * vertically, and diagonally, in that order, with respect to the S2 (i,j) coordinate system. If
-     * the diagonal map cell does not exist (i.e., the location is near an S2 cube vertex), its
-     * corresponding ID is set to zero.
+     * <p>The first map cell, denoted z11 in the appendix of the referenced paper above, contains
+     * the location. The others are the map cells denoted z21, z12, and z22, in that order.
      */
-    @NonNull
-    private static long[] findMapSquare(@NonNull MapParamsProto params,
+    private static long[] findMapSquare(@NonNull MapParamsProto geoidHeightParams,
             @NonNull Location location) {
         long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(),
                 location.getLongitude());
 
         // Cell-space properties and coordinates.
-        int sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.mapS2Level);
+        int sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - geoidHeightParams.mapS2Level);
         int maxIj = 1 << S2CellIdUtils.MAX_LEVEL;
-        long s0 = S2CellIdUtils.getParent(s2CellId, params.mapS2Level);
-        int f0 = S2CellIdUtils.getFace(s2CellId);
-        int i0 = S2CellIdUtils.getI(s2CellId);
-        int j0 = S2CellIdUtils.getJ(s2CellId);
-        int i1 = i0 + sizeIj;
-        int j1 = j0 + sizeIj;
+        long z11 = S2CellIdUtils.getParent(s2CellId, geoidHeightParams.mapS2Level);
+        int f11 = S2CellIdUtils.getFace(s2CellId);
+        int i1 = S2CellIdUtils.getI(s2CellId);
+        int j1 = S2CellIdUtils.getJ(s2CellId);
+        int i2 = i1 + sizeIj;
+        int j2 = j1 + sizeIj;
 
         // Non-boundary region calculation - simplest and most common case.
-        if (i1 < maxIj && j1 < maxIj) {
-            return new long[]{
-                    s0,
-                    S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i1, j0), params.mapS2Level),
-                    S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i0, j1), params.mapS2Level),
-                    S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f0, i1, j1), params.mapS2Level)
-            };
+        if (i2 < maxIj && j2 < maxIj) {
+            return new long[]{z11, S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j1),
+                    geoidHeightParams.mapS2Level), S2CellIdUtils.getParent(
+                    S2CellIdUtils.fromFij(f11, i1, j2), geoidHeightParams.mapS2Level),
+                    S2CellIdUtils.getParent(S2CellIdUtils.fromFij(f11, i2, j2),
+                            geoidHeightParams.mapS2Level)};
         }
 
-        // Boundary region calculation.
+        // Boundary region calculation
         long[] edgeNeighbors = new long[4];
-        S2CellIdUtils.getEdgeNeighbors(s0, edgeNeighbors);
-        long s1 = edgeNeighbors[1];
-        long s2 = edgeNeighbors[2];
-        long s3;
-        if (f0 % 2 == 1) {
-            S2CellIdUtils.getEdgeNeighbors(s1, edgeNeighbors);
-            if (i1 < maxIj) {
-                s3 = edgeNeighbors[2];
-            } else {
-                s3 = s1;
-                s1 = edgeNeighbors[1];
-            }
-        } else {
-            S2CellIdUtils.getEdgeNeighbors(s2, edgeNeighbors);
-            if (j1 < maxIj) {
-                s3 = edgeNeighbors[1];
-            } else {
-                s3 = s2;
-                s2 = edgeNeighbors[3];
-            }
-        }
+        S2CellIdUtils.getEdgeNeighbors(z11, edgeNeighbors);
+        long z11W = edgeNeighbors[0];
+        long z11S = edgeNeighbors[1];
+        long z11E = edgeNeighbors[2];
+        long z11N = edgeNeighbors[3];
+
+        long[] otherEdgeNeighbors = new long[4];
+        S2CellIdUtils.getEdgeNeighbors(z11W, otherEdgeNeighbors);
+        S2CellIdUtils.getEdgeNeighbors(z11S, edgeNeighbors);
+        long z11Sw = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+        S2CellIdUtils.getEdgeNeighbors(z11E, otherEdgeNeighbors);
+        long z11Se = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+        S2CellIdUtils.getEdgeNeighbors(z11N, edgeNeighbors);
+        long z11Ne = findCommonNeighbor(edgeNeighbors, otherEdgeNeighbors, z11);
+
+        long z21 = (f11 % 2 == 1 && i2 >= maxIj) ? z11Sw : z11S;
+        long z12 = (f11 % 2 == 0 && j2 >= maxIj) ? z11Ne : z11E;
+        long z22 = (z21 == z11Sw) ? z11S : (z12 == z11Ne) ? z11E : z11Se;
 
         // Reuse edge neighbors' array to avoid an extra allocation.
-        edgeNeighbors[0] = s0;
-        edgeNeighbors[1] = s1;
-        edgeNeighbors[2] = s2;
-        edgeNeighbors[3] = s3;
+        edgeNeighbors[0] = z11;
+        edgeNeighbors[1] = z21;
+        edgeNeighbors[2] = z12;
+        edgeNeighbors[3] = z22;
         return edgeNeighbors;
     }
 
     /**
+     * Returns the first common non-z11 neighbor found between the two arrays of edge neighbors. If
+     * such a common neighbor does not exist, returns z11.
+     */
+    private static long findCommonNeighbor(long[] edgeNeighbors, long[] otherEdgeNeighbors,
+            long z11) {
+        for (long edgeNeighbor : edgeNeighbors) {
+            if (edgeNeighbor == z11) {
+                continue;
+            }
+            for (long otherEdgeNeighbor : otherEdgeNeighbors) {
+                if (edgeNeighbor == otherEdgeNeighbor) {
+                    return edgeNeighbor;
+                }
+            }
+        }
+        return z11;
+    }
+
+    /**
      * Adds to {@code location} the bilinearly interpolated Mean Sea Level altitude. In addition, a
      * Mean Sea Level altitude accuracy is added if the {@code location} has a valid vertical
      * accuracy; otherwise, does not add a corresponding accuracy.
      */
-    private static void addMslAltitude(@NonNull MapParamsProto params,
+    private static void addMslAltitude(@NonNull MapParamsProto geoidHeightParams,
             @NonNull double[] geoidHeightsMeters, @NonNull Location location) {
         double h0 = geoidHeightsMeters[0];
         double h1 = geoidHeightsMeters[1];
@@ -158,7 +172,7 @@
         // employ the simplified unit square formulation.
         long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(),
                 location.getLongitude());
-        double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - params.mapS2Level);
+        double sizeIj = 1 << (S2CellIdUtils.MAX_LEVEL - geoidHeightParams.mapS2Level);
         double wi = (S2CellIdUtils.getI(s2CellId) % sizeIj) / sizeIj;
         double wj = (S2CellIdUtils.getJ(s2CellId) % sizeIj) / sizeIj;
         double offsetMeters = h0 + (h1 - h0) * wi + (h2 - h0) * wj + (h3 - h1 - h2 + h0) * wi * wj;
@@ -167,8 +181,8 @@
         if (location.hasVerticalAccuracy()) {
             double verticalAccuracyMeters = location.getVerticalAccuracyMeters();
             if (Double.isFinite(verticalAccuracyMeters) && verticalAccuracyMeters >= 0) {
-                location.setMslAltitudeAccuracyMeters(
-                        (float) Math.hypot(verticalAccuracyMeters, params.modelRmseMeters));
+                location.setMslAltitudeAccuracyMeters((float) Math.hypot(verticalAccuracyMeters,
+                        geoidHeightParams.modelRmseMeters));
             }
         }
     }
@@ -191,10 +205,11 @@
     public void addMslAltitudeToLocation(@NonNull Context context, @NonNull Location location)
             throws IOException {
         validate(location);
-        MapParamsProto params = GeoidHeightMap.getParams(context);
-        long[] s2CellIds = findMapSquare(params, location);
-        double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, context, s2CellIds);
-        addMslAltitude(params, geoidHeightsMeters, location);
+        MapParamsProto geoidHeightParams = GeoidMap.getGeoidHeightParams(context);
+        long[] mapCells = findMapSquare(geoidHeightParams, location);
+        double[] geoidHeightsMeters = mGeoidMap.readGeoidHeights(geoidHeightParams, context,
+                mapCells);
+        addMslAltitude(geoidHeightParams, geoidHeightsMeters, location);
     }
 
     /**
@@ -206,18 +221,68 @@
      */
     public boolean addMslAltitudeToLocation(@NonNull Location location) {
         validate(location);
-        MapParamsProto params = GeoidHeightMap.getParams();
-        if (params == null) {
+        MapParamsProto geoidHeightParams = GeoidMap.getGeoidHeightParams();
+        if (geoidHeightParams == null) {
             return false;
         }
 
-        long[] s2CellIds = findMapSquare(params, location);
-        double[] geoidHeightsMeters = mGeoidHeightMap.readGeoidHeights(params, s2CellIds);
+        long[] mapCells = findMapSquare(geoidHeightParams, location);
+        double[] geoidHeightsMeters = mGeoidMap.readGeoidHeights(geoidHeightParams, mapCells);
         if (geoidHeightsMeters == null) {
             return false;
         }
 
-        addMslAltitude(params, geoidHeightsMeters, location);
+        addMslAltitude(geoidHeightParams, geoidHeightsMeters, location);
         return true;
     }
+
+    /**
+     * Returns the geoid height (a.k.a. geoid undulation) at the location specified in {@code
+     * request}. The geoid height at a location is defined as the difference between an altitude
+     * measured above the World Geodetic System 1984 reference ellipsoid (WGS84) and its
+     * corresponding Mean Sea Level altitude.
+     *
+     * <p>Must be called off the main thread as data may be loaded from raw assets.
+     *
+     * @throws IOException              if an I/O error occurs when loading data from raw assets.
+     * @throws IllegalArgumentException if the {@code request} has an invalid latitude or longitude.
+     *                                  Specifically, the latitude must be between -90 and 90 (both
+     *                                  inclusive), and the longitude must be between -180 and 180
+     *                                  (both inclusive).
+     * @hide
+     */
+    @WorkerThread
+    public @NonNull GetGeoidHeightResponse getGeoidHeight(@NonNull Context context,
+            @NonNull GetGeoidHeightRequest request) throws IOException {
+        // Create a valid location from which the geoid height and its accuracy will be extracted.
+        Location location = new Location("");
+        location.setLatitude(request.latitudeDegrees);
+        location.setLongitude(request.longitudeDegrees);
+        location.setAltitude(0.0);
+        location.setVerticalAccuracyMeters(0.0f);
+
+        addMslAltitudeToLocation(context, location);
+        // The geoid height for a location with zero WGS84 altitude is equal in value to the
+        // negative of corresponding MSL altitude.
+        double geoidHeightMeters = -location.getMslAltitudeMeters();
+        // The geoid height error for a location with zero vertical accuracy is equal in value to
+        // the corresponding MSL altitude accuracy.
+        float geoidHeightErrorMeters = location.getMslAltitudeAccuracyMeters();
+
+        MapParamsProto expirationDistanceParams = GeoidMap.getExpirationDistanceParams(context);
+        long s2CellId = S2CellIdUtils.fromLatLngDegrees(location.getLatitude(),
+                location.getLongitude());
+        long[] mapCell = {S2CellIdUtils.getParent(s2CellId, expirationDistanceParams.mapS2Level)};
+        double expirationDistanceMeters = mGeoidMap.readExpirationDistances(
+                expirationDistanceParams, context, mapCell)[0];
+        float additionalGeoidHeightErrorMeters = (float) expirationDistanceParams.modelRmseMeters;
+
+        GetGeoidHeightResponse response = new GetGeoidHeightResponse();
+        response.geoidHeightMeters = geoidHeightMeters;
+        response.geoidHeightErrorMeters = geoidHeightErrorMeters;
+        response.expirationDistanceMeters = expirationDistanceMeters;
+        response.additionalGeoidHeightErrorMeters = additionalGeoidHeightErrorMeters;
+        response.success = true;
+        return response;
+    }
 }
diff --git a/location/java/com/android/internal/location/altitude/GeoidHeightMap.java b/location/java/com/android/internal/location/altitude/GeoidMap.java
similarity index 65%
rename from location/java/com/android/internal/location/altitude/GeoidHeightMap.java
rename to location/java/com/android/internal/location/altitude/GeoidMap.java
index 8067050..9bf5689 100644
--- a/location/java/com/android/internal/location/altitude/GeoidHeightMap.java
+++ b/location/java/com/android/internal/location/altitude/GeoidMap.java
@@ -34,11 +34,12 @@
 import java.util.Objects;
 
 /**
- * Manages a mapping of geoid heights associated with S2 cells, referred to as MAP CELLS.
+ * Manages a mapping of geoid heights and expiration distances associated with S2 cells, referred to
+ * as MAP CELLS.
  *
  * <p>Tiles are used extensively to reduce the number of entries needed to be stored in memory and
- * on disk. A tile associates geoid heights with all map cells of a common parent at a specified S2
- * level.
+ * on disk. A tile associates geoid heights or expiration distances with all map cells of a common
+ * parent at a specified S2 level.
  *
  * <p>Since bilinear interpolation considers at most four map cells at a time, at most four tiles
  * are simultaneously stored in memory. These tiles, referred to as CACHE TILES, are each keyed by
@@ -48,42 +49,79 @@
  * The latter tiles, referred to as DISK TILES, are each keyed by its common parent's S2 cell token,
  * referred to as a DISK TOKEN.
  */
-public final class GeoidHeightMap {
+public final class GeoidMap {
 
-    private static final Object sLock = new Object();
+    private static final Object GEOID_HEIGHT_PARAMS_LOCK = new Object();
 
-    @GuardedBy("sLock")
+    private static final Object EXPIRATION_DISTANCE_PARAMS_LOCK = new Object();
+
+    @GuardedBy("GEOID_HEIGHT_PARAMS_LOCK")
     @Nullable
-    private static MapParamsProto sParams;
+    private static MapParamsProto sGeoidHeightParams;
 
-    /** Defines a cache large enough to hold all cache tiles needed for interpolation. */
-    private final LruCache<Long, S2TileProto> mCacheTiles = new LruCache<>(4);
+    @GuardedBy("EXPIRATION_DISTANCE_PARAMS_LOCK")
+    @Nullable
+    private static MapParamsProto sExpirationDistanceParams;
 
     /**
-     * Returns the singleton parameter instance for a spherically projected geoid height map and its
-     * corresponding tile management.
+     * Defines a cache large enough to hold all geoid height cache tiles needed for interpolation.
+     */
+    private final LruCache<Long, S2TileProto> mGeoidHeightCacheTiles = new LruCache<>(4);
+
+    /**
+     * Defines a cache large enough to hold all expiration distance cache tiles needed for
+     * interpolation.
+     */
+    private final LruCache<Long, S2TileProto> mExpirationDistanceCacheTiles = new LruCache<>(4);
+
+    /**
+     * Returns the singleton parameter instance for geoid height parameters of a spherically
+     * projected map.
      */
     @NonNull
-    public static MapParamsProto getParams(@NonNull Context context) throws IOException {
-        synchronized (sLock) {
-            if (sParams == null) {
-                try (InputStream is = context.getApplicationContext().getAssets().open(
-                        "geoid_height_map/map-params.pb")) {
-                    sParams = MapParamsProto.parseFrom(is.readAllBytes());
-                }
+    public static MapParamsProto getGeoidHeightParams(@NonNull Context context) throws IOException {
+        synchronized (GEOID_HEIGHT_PARAMS_LOCK) {
+            if (sGeoidHeightParams == null) {
+                // TODO: b/304375846 - Configure with disk tile prefix once resources are updated.
+                sGeoidHeightParams = parseParams(context);
             }
-            return sParams;
+            return sGeoidHeightParams;
         }
     }
 
     /**
-     * Same as {@link #getParams(Context)} except that null is returned if the singleton parameter
-     * instance is not yet initialized.
+     * Returns the singleton parameter instance for expiration distance parameters of a spherically
+     * projected
+     * map.
+     */
+    @NonNull
+    public static MapParamsProto getExpirationDistanceParams(@NonNull Context context)
+            throws IOException {
+        synchronized (EXPIRATION_DISTANCE_PARAMS_LOCK) {
+            if (sExpirationDistanceParams == null) {
+                // TODO: b/304375846 - Configure with disk tile prefix once resources are updated.
+                sExpirationDistanceParams = parseParams(context);
+            }
+            return sExpirationDistanceParams;
+        }
+    }
+
+    @NonNull
+    private static MapParamsProto parseParams(@NonNull Context context) throws IOException {
+        try (InputStream is = context.getApplicationContext().getAssets().open(
+                "geoid_height_map/map-params.pb")) {
+            return MapParamsProto.parseFrom(is.readAllBytes());
+        }
+    }
+
+    /**
+     * Same as {@link #getGeoidHeightParams(Context)} except that null is returned if the singleton
+     * parameter instance is not yet initialized.
      */
     @Nullable
-    public static MapParamsProto getParams() {
-        synchronized (sLock) {
-            return sParams;
+    public static MapParamsProto getGeoidHeightParams() {
+        synchronized (GEOID_HEIGHT_PARAMS_LOCK) {
+            return sGeoidHeightParams;
         }
     }
 
@@ -93,18 +131,17 @@
 
     @NonNull
     private static String getDiskToken(@NonNull MapParamsProto params, long s2CellId) {
-        return S2CellIdUtils.getToken(
-                S2CellIdUtils.getParent(s2CellId, params.diskTileS2Level));
+        return S2CellIdUtils.getToken(S2CellIdUtils.getParent(s2CellId, params.diskTileS2Level));
     }
 
     /**
      * Adds to {@code values} values in the unit interval [0, 1] for the map cells identified by
-     * {@code s2CellIds}. Returns true if values are present for all IDs; otherwise, returns false
-     * and adds NaNs for absent values.
+     * {@code s2CellIds}. Returns true if values are present for all IDs; otherwise, adds NaNs for
+     * absent values and returns false.
      */
     private static boolean getUnitIntervalValues(@NonNull MapParamsProto params,
-            @NonNull TileFunction tileFunction,
-            @NonNull long[] s2CellIds, @NonNull double[] values) {
+            @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds,
+            @NonNull double[] values) {
         int len = s2CellIds.length;
 
         S2TileProto[] tiles = new S2TileProto[len];
@@ -137,9 +174,8 @@
 
     @SuppressWarnings("ReferenceEquality")
     private static void mergeByteBufferValues(@NonNull MapParamsProto params,
-            @NonNull long[] s2CellIds,
-            @NonNull S2TileProto[] tiles,
-            int tileIndex, @NonNull double[] values) {
+            @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex,
+            @NonNull double[] values) {
         byte[] bytes = tiles[tileIndex].byteBuffer;
         if (bytes == null || bytes.length == 0) {
             return;
@@ -163,24 +199,22 @@
     }
 
     private static void mergeByteJpegValues(@NonNull MapParamsProto params,
-            @NonNull long[] s2CellIds,
-            @NonNull S2TileProto[] tiles,
-            int tileIndex, @NonNull double[] values) {
+            @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex,
+            @NonNull double[] values) {
         mergeByteImageValues(params, tiles[tileIndex].byteJpeg, s2CellIds, tiles, tileIndex,
                 values);
     }
 
     private static void mergeBytePngValues(@NonNull MapParamsProto params,
-            @NonNull long[] s2CellIds,
-            @NonNull S2TileProto[] tiles,
-            int tileIndex, @NonNull double[] values) {
+            @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex,
+            @NonNull double[] values) {
         mergeByteImageValues(params, tiles[tileIndex].bytePng, s2CellIds, tiles, tileIndex, values);
     }
 
     @SuppressWarnings("ReferenceEquality")
     private static void mergeByteImageValues(@NonNull MapParamsProto params, @NonNull byte[] bytes,
-            @NonNull long[] s2CellIds,
-            @NonNull S2TileProto[] tiles, int tileIndex, @NonNull double[] values) {
+            @NonNull long[] s2CellIds, @NonNull S2TileProto[] tiles, int tileIndex,
+            @NonNull double[] values) {
         if (bytes == null || bytes.length == 0) {
             return;
         }
@@ -219,7 +253,7 @@
      * ID.
      */
     private static void validate(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) {
-        Preconditions.checkArgument(s2CellIds.length == 4);
+        Preconditions.checkArgument(s2CellIds.length <= 4);
         for (long s2CellId : s2CellIds) {
             Preconditions.checkArgument(S2CellIdUtils.getLevel(s2CellId) == params.mapS2Level);
         }
@@ -233,15 +267,38 @@
     @NonNull
     public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull Context context,
             @NonNull long[] s2CellIds) throws IOException {
+        return readMapValues(params, context, s2CellIds, mGeoidHeightCacheTiles);
+    }
+
+    /**
+     * Returns the expiration distances in meters associated with the map cells identified by
+     * {@code s2CellIds}. Throws an {@link IOException} if a geoid height cannot be calculated for
+     * an ID.
+     */
+    @NonNull
+    public double[] readExpirationDistances(@NonNull MapParamsProto params,
+            @NonNull Context context, @NonNull long[] s2CellIds) throws IOException {
+        return readMapValues(params, context, s2CellIds, mExpirationDistanceCacheTiles);
+    }
+
+    /**
+     * Returns the map values in meters associated with the map cells identified by
+     * {@code s2CellIds}. Throws an {@link IOException} if a map value cannot be calculated for an
+     * ID.
+     */
+    @NonNull
+    private static double[] readMapValues(@NonNull MapParamsProto params, @NonNull Context context,
+            @NonNull long[] s2CellIds, @NonNull LruCache<Long, S2TileProto> cacheTiles)
+            throws IOException {
         validate(params, s2CellIds);
-        double[] heightsMeters = new double[s2CellIds.length];
-        if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
-            return heightsMeters;
+        double[] mapValuesMeters = new double[s2CellIds.length];
+        if (getMapValues(params, cacheTiles::get, s2CellIds, mapValuesMeters)) {
+            return mapValuesMeters;
         }
 
-        TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds);
-        if (getGeoidHeights(params, loadedTiles, s2CellIds, heightsMeters)) {
-            return heightsMeters;
+        TileFunction loadedTiles = loadFromCacheAndDisk(params, context, s2CellIds, cacheTiles);
+        if (getMapValues(params, loadedTiles, s2CellIds, mapValuesMeters)) {
+            return mapValuesMeters;
         }
         throw new IOException("Unable to calculate geoid heights from raw assets.");
     }
@@ -255,32 +312,33 @@
     public double[] readGeoidHeights(@NonNull MapParamsProto params, @NonNull long[] s2CellIds) {
         validate(params, s2CellIds);
         double[] heightsMeters = new double[s2CellIds.length];
-        if (getGeoidHeights(params, mCacheTiles::get, s2CellIds, heightsMeters)) {
+        if (getMapValues(params, mGeoidHeightCacheTiles::get, s2CellIds, heightsMeters)) {
             return heightsMeters;
         }
         return null;
     }
 
     /**
-     * Adds to {@code heightsMeters} the geoid heights in meters associated with the map cells
+     * Adds to {@code mapValuesMeters} the map values in meters associated with the map cells
      * identified by {@code s2CellIds}. Returns true if heights are present for all IDs; otherwise,
-     * returns false and adds NaNs for absent heights.
+     * adds NaNs for absent heights and returns false.
      */
-    private boolean getGeoidHeights(@NonNull MapParamsProto params,
+    private static boolean getMapValues(@NonNull MapParamsProto params,
             @NonNull TileFunction tileFunction, @NonNull long[] s2CellIds,
-            @NonNull double[] heightsMeters) {
-        boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, heightsMeters);
-        for (int i = 0; i < heightsMeters.length; i++) {
+            @NonNull double[] mapValuesMeters) {
+        boolean allFound = getUnitIntervalValues(params, tileFunction, s2CellIds, mapValuesMeters);
+        for (int i = 0; i < mapValuesMeters.length; i++) {
             // NaNs are properly preserved.
-            heightsMeters[i] *= params.modelAMeters;
-            heightsMeters[i] += params.modelBMeters;
+            mapValuesMeters[i] *= params.modelAMeters;
+            mapValuesMeters[i] += params.modelBMeters;
         }
         return allFound;
     }
 
     @NonNull
-    private TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params,
-            @NonNull Context context, @NonNull long[] s2CellIds) throws IOException {
+    private static TileFunction loadFromCacheAndDisk(@NonNull MapParamsProto params,
+            @NonNull Context context, @NonNull long[] s2CellIds,
+            @NonNull LruCache<Long, S2TileProto> cacheTiles) throws IOException {
         int len = s2CellIds.length;
 
         // Enable batch loading by finding all cache keys upfront.
@@ -296,7 +354,7 @@
             if (diskTokens[i] != null) {
                 continue;
             }
-            loadedTiles[i] = mCacheTiles.get(cacheKeys[i]);
+            loadedTiles[i] = cacheTiles.get(cacheKeys[i]);
             diskTokens[i] = getDiskToken(params, cacheKeys[i]);
 
             // Batch across common cache key.
@@ -319,7 +377,7 @@
                     "geoid_height_map/tile-" + diskTokens[i] + ".pb")) {
                 tile = S2TileProto.parseFrom(is.readAllBytes());
             }
-            mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles);
+            mergeFromDiskTile(params, tile, cacheKeys, diskTokens, i, loadedTiles, cacheTiles);
         }
 
         return cacheKey -> {
@@ -332,9 +390,10 @@
         };
     }
 
-    private void mergeFromDiskTile(@NonNull MapParamsProto params, @NonNull S2TileProto diskTile,
-            @NonNull long[] cacheKeys, @NonNull String[] diskTokens, int diskTokenIndex,
-            @NonNull S2TileProto[] loadedTiles) throws IOException {
+    private static void mergeFromDiskTile(@NonNull MapParamsProto params,
+            @NonNull S2TileProto diskTile, @NonNull long[] cacheKeys, @NonNull String[] diskTokens,
+            int diskTokenIndex, @NonNull S2TileProto[] loadedTiles,
+            @NonNull LruCache<Long, S2TileProto> cacheTiles) throws IOException {
         int len = cacheKeys.length;
         int numMapCellsPerCacheTile = 1 << (2 * (params.mapS2Level - params.cacheTileS2Level));
 
@@ -375,7 +434,7 @@
             }
 
             // Side load into tile cache.
-            mCacheTiles.put(cacheKeys[i], loadedTiles[i]);
+            cacheTiles.put(cacheKeys[i], loadedTiles[i]);
         }
     }
 
diff --git a/nfc/jarjar-rules.txt b/nfc/jarjar-rules.txt
index 4cd652d..99ae144 100644
--- a/nfc/jarjar-rules.txt
+++ b/nfc/jarjar-rules.txt
@@ -4,6 +4,7 @@
 rule android.content.IntentProto* com.android.nfc.x.@0
 rule android.content.IntentFilterProto* com.android.nfc.x.@0
 rule android.content.AuthorityEntryProto* com.android.nfc.x.@0
+rule android.content.UriRelativeFilter* com.android.nfc.x.@0
 rule android.nfc.cardemulation.AidGroupProto* com.android.nfc.x.@0
 rule android.nfc.cardemulation.ApduServiceInfoProto* com.android.nfc.x.@0
 rule android.nfc.cardemulation.NfcFServiceInfoProto* com.android.nfc.x.@0
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 9d38e4c..1f41b81 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -338,8 +338,10 @@
         }
     }
     /**
-     * Sets whether the system should default to observe mode or not when
-     * the service is in the foreground or the default payment service.
+     * Sets whether the system should default to observe mode or not when the service is in the
+     * foreground or the default payment service. The default is to not enable observe mode when
+     * a service either the foreground default service or the default payment service so not
+     * calling this method will preserve that behavior.
      *
      * @param service The component name of the service
      * @param enable Whether the servic should default to observe mode or not
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
new file mode 100644
index 0000000..5636266
--- /dev/null
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.crashrecovery.flags"
+
+flag {
+    name: "recoverability_detection"
+    namespace: "package_watchdog"
+    description: "Feature flag for recoverability detection"
+    bug: "310236690"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
index e099f11..0a424bc 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt
@@ -37,10 +37,11 @@
 fun Context.broadcastReceiverFlow(intentFilter: IntentFilter): Flow<Intent> = callbackFlow {
     val broadcastReceiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
+            Log.d(TAG, "onReceive: $intent")
             trySend(intent)
         }
     }
-    registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
+    registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_VISIBLE_TO_INSTANT_APPS)
 
     awaitClose { unregisterReceiver(broadcastReceiver) }
 }.catch { e ->
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
index eef5225..772f925 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt
@@ -43,7 +43,7 @@
 
     private val context = mock<Context> {
         on {
-            registerReceiver(any(), eq(INTENT_FILTER), eq(Context.RECEIVER_NOT_EXPORTED))
+            registerReceiver(any(), eq(INTENT_FILTER), eq(Context.RECEIVER_VISIBLE_TO_INSTANT_APPS))
         } doAnswer {
             registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
             null
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
new file mode 100644
index 0000000..3355fb3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.volume.data.repository
+
+import android.media.AudioManager
+import com.android.internal.util.ConcurrentUtils
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Provides audio managing functionality and data. */
+interface AudioRepository {
+
+    /** Current [AudioManager.getMode]. */
+    val mode: StateFlow<Int>
+}
+
+class AudioRepositoryImpl(
+    private val audioManager: AudioManager,
+    backgroundCoroutineContext: CoroutineContext,
+    coroutineScope: CoroutineScope,
+) : AudioRepository {
+
+    override val mode: StateFlow<Int> =
+        callbackFlow {
+                val listener =
+                    AudioManager.OnModeChangedListener { newMode -> launch { send(newMode) } }
+                audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener)
+                awaitClose { audioManager.removeOnModeChangedListener(listener) }
+            }
+            .flowOn(backgroundCoroutineContext)
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.kt
new file mode 100644
index 0000000..053c59b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractor.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.settingslib.volume.domain.interactor
+
+import android.media.AudioManager
+import com.android.settingslib.volume.data.repository.AudioRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class AudioModeInteractor(repository: AudioRepository) {
+
+    private val ongoingCallModes =
+        setOf(
+            AudioManager.MODE_RINGTONE,
+            AudioManager.MODE_IN_CALL,
+            AudioManager.MODE_IN_COMMUNICATION,
+        )
+
+    /** Returns if current [AudioManager.getMode] call is an ongoing call */
+    val isOngoingCall: Flow<Boolean> = repository.mode.map { it in ongoingCallModes }
+}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 644b72c..ce3a7ba 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -57,6 +57,7 @@
         "SettingsLibSettingsSpinner",
         "SettingsLibUsageProgressBarPreference",
         "settingslib_media_flags_lib",
+        "kotlinx_coroutines_test",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt
new file mode 100644
index 0000000..686362f
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.volume.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeAudioRepository : AudioRepository {
+
+    private val mutableMode = MutableStateFlow(0)
+    override val mode: StateFlow<Int>
+        get() = mutableMode.asStateFlow()
+
+    fun setMode(newMode: Int) {
+        mutableMode.value = newMode
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt
new file mode 100644
index 0000000..3bc1edc
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.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.settingslib.volume.domain.interactor
+
+import android.media.AudioManager
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.settingslib.volume.data.repository.FakeAudioRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AudioModeInteractorTest {
+
+    private val testScope = TestScope()
+    private val fakeAudioRepository = FakeAudioRepository()
+
+    private val underTest = AudioModeInteractor(fakeAudioRepository)
+
+    @Test
+    fun ongoingCallModes_isOnGoingCall() {
+        testScope.runTest {
+            for (mode in ongoingCallModes) {
+                var isOngoingCall = false
+                underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope)
+
+                fakeAudioRepository.setMode(mode)
+                runCurrent()
+
+                assertThat(isOngoingCall).isTrue()
+            }
+        }
+    }
+
+    @Test
+    fun notOngoingCallModes_isNotOnGoingCall() {
+        testScope.runTest {
+            var isOngoingCall = true
+            underTest.isOngoingCall.onEach { isOngoingCall = it }.launchIn(backgroundScope)
+
+            fakeAudioRepository.setMode(AudioManager.MODE_CURRENT)
+            runCurrent()
+
+            assertThat(isOngoingCall).isFalse()
+        }
+    }
+
+    private companion object {
+        private val ongoingCallModes =
+            setOf(
+                AudioManager.MODE_RINGTONE,
+                AudioManager.MODE_IN_CALL,
+                AudioManager.MODE_IN_COMMUNICATION,
+            )
+    }
+}
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3839dd9..307a619 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -29,6 +29,9 @@
     <color name="status_bar_icons_hover_color_light">#38FFFFFF</color> <!-- 22% white -->
     <color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black -->
 
+    <!-- The dark background color behind the shade -->
+    <color name="shade_scrim_background_dark">@*android:color/black</color>
+
     <!-- The color of the background in the separated list of the Global Actions menu -->
     <color name="global_actions_separated_background">#F5F5F5</color>
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
deleted file mode 100644
index 5deea9b..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2015 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.keyboard;
-
-import android.content.Context;
-import android.view.WindowManager;
-
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-
-public class BluetoothDialog extends SystemUIDialog {
-
-    public BluetoothDialog(Context context) {
-        super(context);
-
-        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
-        setShowForAllUsers(true);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialogDelegate.java
new file mode 100644
index 0000000..98642d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialogDelegate.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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.keyboard;
+
+import android.view.WindowManager;
+
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import javax.inject.Inject;
+
+public class BluetoothDialogDelegate implements SystemUIDialog.Delegate{
+
+    private final SystemUIDialog.Factory mSystemUIDialogFactory;
+    @Inject
+    public BluetoothDialogDelegate(SystemUIDialog.Factory systemUIDialogFactory) {
+        mSystemUIDialogFactory = systemUIDialogFactory;
+    }
+
+    @Override
+    public SystemUIDialog createDialog() {
+        SystemUIDialog dialog = mSystemUIDialogFactory.create(this);
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        dialog.setShowForAllUsers(true);
+        return dialog;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 1cdbe6f..17e3ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -52,6 +52,7 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.PrintWriter;
@@ -109,6 +110,7 @@
 
     private final Provider<LocalBluetoothManager> mBluetoothManagerProvider;
     private final SecureSettings mSecureSettings;
+    private final BluetoothDialogDelegate mBluetoothDialogDelegate;
 
     private boolean mEnabled;
     private String mKeyboardName;
@@ -121,16 +123,20 @@
     private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN;
     private int mScanAttempt = 0;
     private ScanCallback mScanCallback;
-    private BluetoothDialog mDialog;
+    private SystemUIDialog mDialog;
 
     private int mState;
 
     @Inject
-    public KeyboardUI(Context context, Provider<LocalBluetoothManager> bluetoothManagerProvider,
-            SecureSettings secureSettings) {
+    public KeyboardUI(
+            Context context,
+            Provider<LocalBluetoothManager> bluetoothManagerProvider,
+            SecureSettings secureSettings,
+            BluetoothDialogDelegate bluetoothDialogDelegate) {
         mContext = context;
         this.mBluetoothManagerProvider = bluetoothManagerProvider;
         mSecureSettings = secureSettings;
+        mBluetoothDialogDelegate = bluetoothDialogDelegate;
     }
 
     @Override
@@ -437,7 +443,7 @@
                             new BluetoothDialogClickListener();
                     DialogInterface.OnDismissListener dismissListener =
                             new BluetoothDialogDismissListener();
-                    mDialog = new BluetoothDialog(mContext);
+                    mDialog = mBluetoothDialogDelegate.createDialog();
                     mDialog.setTitle(R.string.enable_bluetooth_title);
                     mDialog.setMessage(R.string.enable_bluetooth_message);
                     mDialog.setPositiveButton(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt
index 4ed8120..5ef5ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt
@@ -43,4 +43,13 @@
             { "new sticky keys state received: $str1" }
         )
     }
-}
\ No newline at end of file
+
+    fun logNewSettingValue(enabled: Boolean) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            { bool1 = enabled },
+            { "sticky key setting changed, new state: ${if (bool1) "enabled" else "disabled"}" }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
index 34d2888..ec29bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
@@ -19,8 +19,10 @@
 import android.hardware.input.InputManager
 import android.hardware.input.InputManager.StickyModifierStateListener
 import android.hardware.input.StickyModifierState
+import android.provider.Settings
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
 import com.android.systemui.keyboard.stickykeys.shared.model.Locked
@@ -30,14 +32,19 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
 import javax.inject.Inject
 
 interface StickyKeysRepository {
@@ -45,11 +52,15 @@
     val settingEnabled: Flow<Boolean>
 }
 
+@SysUISingleton
+@OptIn(ExperimentalCoroutinesApi::class)
 class StickyKeysRepositoryImpl
 @Inject
 constructor(
     private val inputManager: InputManager,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val secureSettings: SecureSettings,
+    userRepository: UserRepository,
     private val stickyKeysLogger: StickyKeysLogger,
 ) : StickyKeysRepository {
 
@@ -66,8 +77,26 @@
             .onEach { stickyKeysLogger.logNewStickyKeysReceived(it) }
             .flowOn(backgroundDispatcher)
 
-    // TODO(b/319837892): Implement reading actual setting
-    override val settingEnabled: StateFlow<Boolean> = MutableStateFlow(true)
+    override val settingEnabled: Flow<Boolean> =
+        userRepository.selectedUserInfo
+            .flatMapLatest { stickyKeySettingObserver(it.id) }
+            .flowOn(backgroundDispatcher)
+
+    private fun stickyKeySettingObserver(userId: Int): Flow<Boolean> {
+        return secureSettings
+            .observerFlow(userId, SETTING_KEY)
+            .onStart { emit(Unit) }
+            .map { isSettingEnabledForCurrentUser(userId) }
+            .distinctUntilChanged()
+            .onEach { stickyKeysLogger.logNewSettingValue(it) }
+    }
+
+    private fun isSettingEnabledForCurrentUser(userId: Int) =
+        secureSettings.getIntForUser(
+            /* name= */ SETTING_KEY,
+            /* default= */ 0,
+            /* userHandle= */ userId
+        ) != 0
 
     private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> {
         val keys = linkedMapOf<ModifierKey, Locked>()
@@ -88,5 +117,6 @@
 
     companion object {
         const val TAG = "StickyKeysRepositoryImpl"
+        const val SETTING_KEY = Settings.Secure.ACCESSIBILITY_STICKY_KEYS
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 93a6eee..b3d848c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -371,7 +371,6 @@
                 // Receiving a CANCEL implies that something else intercepted
                 // the gesture, i.e., the user did not cancel their gesture.
                 // Therefore, disappear immediately, with minimum fanfare.
-                interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW)
                 updateArrowState(GestureState.GONE)
                 velocityTracker = null
             }
@@ -883,16 +882,6 @@
         previousState = currentState
         currentState = newState
 
-        // First, update the jank tracker
-        when (currentState) {
-            GestureState.ENTRY -> {
-                interactionJankMonitor.cancel(CUJ_BACK_PANEL_ARROW)
-                interactionJankMonitor.begin(mView, CUJ_BACK_PANEL_ARROW)
-            }
-            GestureState.GONE -> interactionJankMonitor.end(CUJ_BACK_PANEL_ARROW)
-            else -> {}
-        }
-
         when (currentState) {
             GestureState.CANCELLED -> {
                 backCallback.cancelBack()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a9dd25b..f173900 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static android.app.Flags.keyguardPrivateNotifications;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
@@ -81,6 +82,7 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.ScreenshotRequest;
 import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
@@ -167,9 +169,10 @@
     private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder;
     private final UiEventLogger mUiEventLogger;
     private final DisplayTracker mDisplayTracker;
-
     private Region mActiveNavBarRegion;
 
+    private final BroadcastDispatcher mBroadcastDispatcher;
+
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
     private boolean mBound;
@@ -419,6 +422,21 @@
         retryConnectionWithBackoff();
     };
 
+    private final BroadcastReceiver mUserEventReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Objects.equals(intent.getAction(), Intent.ACTION_USER_UNLOCKED)) {
+                if (keyguardPrivateNotifications()) {
+                    // Start the overview connection to the launcher service
+                    // Connect if user hasn't connected yet
+                    if (getProxy() == null) {
+                        startConnectionToCurrentUser();
+                    }
+                }
+            }
+        }
+    };
+
     private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -586,7 +604,8 @@
             FeatureFlags featureFlags,
             SceneContainerFlags sceneContainerFlags,
             DumpManager dumpManager,
-            Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder
+            Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder,
+            BroadcastDispatcher broadcastDispatcher
     ) {
         // b/241601880: This component shouldn't be running for a non-primary user
         if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
@@ -615,6 +634,7 @@
         mUiEventLogger = uiEventLogger;
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
+        mBroadcastDispatcher = broadcastDispatcher;
 
         if (!KeyguardWmStateRefactor.isEnabled()) {
             mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
@@ -635,6 +655,12 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
 
+        if (keyguardPrivateNotifications()) {
+            mBroadcastDispatcher.registerReceiver(mUserEventReceiver,
+                    new IntentFilter(Intent.ACTION_USER_UNLOCKED),
+                    null /* executor */, UserHandle.ALL);
+        }
+
         // Listen for status bar state changes
         statusBarWinController.registerCallback(mStatusBarWindowCallback);
         mScreenshotHelper = new ScreenshotHelper(context);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 24ac70e..2a4753d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -234,10 +234,12 @@
             } else if (profileAvailabilityActions(action)) {
                 updateCurrentProfilesCache();
             } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
-                // Start the overview connection to the launcher service
-                // Connect if user hasn't connected yet
-                if (mOverviewProxyServiceLazy.get().getProxy() == null) {
-                    mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+                if (!keyguardPrivateNotifications()) {
+                    // Start the overview connection to the launcher service
+                    // Connect if user hasn't connected yet
+                    if (mOverviewProxyServiceLazy.get().getProxy() == null) {
+                        mOverviewProxyServiceLazy.get().startConnectionToCurrentUser();
+                    }
                 }
             } else if (Objects.equals(action, NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION)) {
                 final IntentSender intentSender = intent.getParcelableExtra(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index e3b65ab..61bd112 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -20,6 +20,7 @@
 import android.os.Trace;
 
 import com.android.systemui.dock.DockManager;
+import com.android.systemui.res.R;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
@@ -39,8 +40,8 @@
     OFF {
         @Override
         public void prepare(ScrimState previousState) {
-            mFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
+            mFrontTint = mBackgroundColor;
+            mBehindTint = mBackgroundColor;
 
             mFrontAlpha = 1f;
             mBehindAlpha = 1f;
@@ -74,15 +75,15 @@
             } else {
                 mAnimationDuration = ScrimController.ANIMATION_DURATION;
             }
-            mFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
-            mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT;
+            mFrontTint = mBackgroundColor;
+            mBehindTint = mBackgroundColor;
+            mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT;
 
             mFrontAlpha = 0;
             mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
             mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
             if (mClipQsScrim) {
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+                updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
             }
         }
     },
@@ -93,10 +94,10 @@
             // notif scrim alpha values are determined by ScrimController#applyState
             // based on the shade expansion
 
-            mFrontTint = Color.BLACK;
+            mFrontTint = mBackgroundColor;
             mFrontAlpha = .66f;
 
-            mBehindTint = Color.BLACK;
+            mBehindTint = mBackgroundColor;
             mBehindAlpha = 1f;
         }
     },
@@ -110,7 +111,7 @@
             mBehindTint = previousState.mBehindTint;
             mBehindAlpha = previousState.mBehindAlpha;
 
-            mFrontTint = Color.BLACK;
+            mFrontTint = mBackgroundColor;
             mFrontAlpha = .66f;
         }
     },
@@ -122,7 +123,7 @@
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
-            mBehindTint = mClipQsScrim ? Color.BLACK : mSurfaceColor;
+            mBehindTint = mClipQsScrim ? mBackgroundColor : mSurfaceColor;
             mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
             mNotifTint = Color.TRANSPARENT;
             mFrontAlpha = 0f;
@@ -154,10 +155,10 @@
             mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
             mNotifAlpha = 1f;
             mFrontAlpha = 0f;
-            mBehindTint = mClipQsScrim ? Color.TRANSPARENT : Color.BLACK;
+            mBehindTint = mClipQsScrim ? Color.TRANSPARENT : mBackgroundColor;
 
             if (mClipQsScrim) {
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+                updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
             }
         }
     },
@@ -184,11 +185,11 @@
             final boolean isDocked = mDockManager.isDocked();
             mBlankScreen = mDisplayRequiresBlanking;
 
-            mFrontTint = Color.BLACK;
+            mFrontTint = mBackgroundColor;
             mFrontAlpha = (alwaysOnEnabled || isDocked || quickPickupEnabled)
                     ? mAodFrontScrimAlpha : 1f;
 
-            mBehindTint = Color.BLACK;
+            mBehindTint = mBackgroundColor;
             mBehindAlpha = ScrimController.TRANSPARENT;
 
             mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
@@ -222,8 +223,8 @@
         @Override
         public void prepare(ScrimState previousState) {
             mFrontAlpha = mAodFrontScrimAlpha;
-            mBehindTint = Color.BLACK;
-            mFrontTint = Color.BLACK;
+            mBehindTint = mBackgroundColor;
+            mFrontTint = mBackgroundColor;
             mBlankScreen = mDisplayRequiresBlanking;
             mAnimationDuration = mWakeLockScreenSensorActive
                     ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
@@ -231,7 +232,7 @@
         @Override
         public float getMaxLightRevealScrimAlpha() {
             return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA
-                : AOD.getMaxLightRevealScrimAlpha();
+                    : AOD.getMaxLightRevealScrimAlpha();
         }
     },
 
@@ -245,7 +246,6 @@
             mBehindAlpha = mClipQsScrim ? 1 : 0;
             mNotifAlpha = 0;
             mFrontAlpha = 0;
-
             mAnimationDuration = mKeyguardFadingAway
                     ? mKeyguardFadingAwayDuration
                     : CentralSurfaces.FADE_KEYGUARD_DURATION;
@@ -259,22 +259,22 @@
                     && !fromAod;
 
             mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.BLACK;
+            mBehindTint = mBackgroundColor;
             mBlankScreen = false;
 
             if (mDisplayRequiresBlanking && previousState == ScrimState.AOD) {
                 // Set all scrims black, before they fade transparent.
-                updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
+                updateScrimColor(mScrimInFront, 1f /* alpha */, mBackgroundColor /* tint */);
+                updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor /* tint */);
 
                 // Scrims should still be black at the end of the transition.
-                mFrontTint = Color.BLACK;
-                mBehindTint = Color.BLACK;
+                mFrontTint = mBackgroundColor;
+                mBehindTint = mBackgroundColor;
                 mBlankScreen = true;
             }
 
             if (mClipQsScrim) {
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+                updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
             }
         }
     },
@@ -283,8 +283,8 @@
         @Override
         public void prepare(ScrimState previousState) {
             mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.BLACK;
-            mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT;
+            mBehindTint = mBackgroundColor;
+            mNotifTint = mClipQsScrim ? mBackgroundColor : Color.TRANSPARENT;
 
             mFrontAlpha = 0;
             mBehindAlpha = mClipQsScrim ? 1 : 0;
@@ -293,7 +293,7 @@
             mBlankScreen = false;
 
             if (mClipQsScrim) {
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
+                updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
             }
         }
     };
@@ -327,9 +327,11 @@
     boolean mKeyguardFadingAway;
     long mKeyguardFadingAwayDuration;
     boolean mClipQsScrim;
+    int mBackgroundColor;
 
     public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters,
             DockManager dockManager) {
+        mBackgroundColor = scrimBehind.getContext().getColor(R.color.shade_scrim_background_dark);
         mScrimInFront = scrimInFront;
         mScrimBehind = scrimBehind;
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
new file mode 100644
index 0000000..8d5e55a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.volume.dagger
+
+import android.media.AudioManager
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+
+/** Dagger module for audio code in the volume package */
+@Module
+interface AudioModule {
+
+    companion object {
+
+        @Provides
+        fun provideAudioRepository(
+            audioManager: AudioManager,
+            @Background coroutineContext: CoroutineContext,
+            @Application coroutineScope: CoroutineScope,
+        ): AudioRepository = AudioRepositoryImpl(audioManager, coroutineContext, coroutineScope)
+
+        @Provides
+        fun provideAudioModeInteractor(repository: AudioRepository): AudioModeInteractor =
+            AudioModeInteractor(repository)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index b1bfbe0..c842e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -60,6 +60,9 @@
 
 /** Dagger Module for code in the volume package. */
 @Module(
+        includes = {
+                AudioModule.class,
+        },
         subcomponents = {
                 VolumePanelComponent.class
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt
new file mode 100644
index 0000000..ed80a86
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepositoryImplTest.kt
@@ -0,0 +1,98 @@
+package com.android.systemui.keyboard.stickykeys.data.repository
+
+import android.content.pm.UserInfo
+import android.hardware.input.InputManager
+import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class StickyKeysRepositoryImplTest : SysuiTestCase() {
+
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+    private val secureSettings = FakeSettings()
+    private val userRepository = Kosmos().fakeUserRepository
+    private lateinit var stickyKeysRepository: StickyKeysRepositoryImpl
+
+    @Before
+    fun setup() {
+        stickyKeysRepository = StickyKeysRepositoryImpl(
+            mock<InputManager>(),
+            dispatcher,
+            secureSettings,
+            userRepository,
+            mock<StickyKeysLogger>()
+        )
+        userRepository.setUserInfos(USER_INFOS)
+        setStickyKeySettingForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
+        setStickyKeySettingForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
+    }
+
+    @Test
+    fun settingEnabledEmitsValueForCurrentUser() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+
+            val enabled by collectLastValue(stickyKeysRepository.settingEnabled)
+
+            assertThat(enabled).isTrue()
+        }
+    }
+
+    @Test
+    fun settingEnabledEmitsNewValueWhenSettingChanges() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+            val enabled by collectValues(stickyKeysRepository.settingEnabled)
+            runCurrent()
+
+            setStickyKeySettingForUser(enabled = false, userInfo = SETTING_ENABLED_USER)
+
+            assertThat(enabled).containsExactly(true, false).inOrder()
+        }
+    }
+
+    @Test
+    fun settingEnabledEmitsValueForNewUserWhenUserChanges() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SETTING_ENABLED_USER)
+            val enabled by collectLastValue(stickyKeysRepository.settingEnabled)
+            runCurrent()
+
+            userRepository.setSelectedUserInfo(SETTING_DISABLED_USER)
+
+            assertThat(enabled).isFalse()
+        }
+    }
+
+    private fun setStickyKeySettingForUser(enabled: Boolean, userInfo: UserInfo) {
+        val newValue = if (enabled) "1" else "0"
+        secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, userInfo.id)
+    }
+
+    private companion object {
+        val SETTING_ENABLED_USER = UserInfo(/* id= */ 0, "user1", /* flags= */ 0)
+        val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
+        val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 8a71368..6eebb6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -18,6 +18,7 @@
 
 import android.hardware.input.InputManager
 import android.hardware.input.StickyModifierState
+import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -31,8 +32,11 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
@@ -57,6 +61,8 @@
     private lateinit var viewModel: StickyKeysIndicatorViewModel
     private val inputManager = mock<InputManager>()
     private val keyboardRepository = FakeKeyboardRepository()
+    private val secureSettings = FakeSettings()
+    private val userRepository = Kosmos().fakeUserRepository
     private val captor =
         ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
 
@@ -65,8 +71,11 @@
         val stickyKeysRepository = StickyKeysRepositoryImpl(
             inputManager,
             dispatcher,
+            secureSettings,
+            userRepository,
             mock<StickyKeysLogger>()
         )
+        setStickyKeySetting(enabled = false)
         viewModel =
             StickyKeysIndicatorViewModel(
                 stickyKeysRepository = stickyKeysRepository,
@@ -76,15 +85,26 @@
     }
 
     @Test
-    fun startsListeningToStickyKeysOnlyWhenKeyboardIsConnected() {
+    fun doesntListenToStickyKeysOnlyWhenKeyboardIsConnected() {
         testScope.runTest {
             collectLastValue(viewModel.indicatorContent)
-            runCurrent()
-            verifyZeroInteractions(inputManager)
 
             keyboardRepository.setIsAnyKeyboardConnected(true)
             runCurrent()
 
+            verifyZeroInteractions(inputManager)
+        }
+    }
+
+    @Test
+    fun startsListeningToStickyKeysOnlyWhenKeyboardIsConnectedAndSettingIsOn() {
+        testScope.runTest {
+            collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeySetting(enabled = true)
+            runCurrent()
+
             verify(inputManager)
                 .registerStickyModifierStateListener(
                     any(),
@@ -93,11 +113,31 @@
         }
     }
 
+    private fun setStickyKeySetting(enabled: Boolean) {
+        val newValue = if (enabled) "1" else "0"
+        val defaultUser = userRepository.getSelectedUserInfo().id
+        secureSettings.putStringForUser(ACCESSIBILITY_STICKY_KEYS, newValue, defaultUser)
+    }
+
+    @Test
+    fun stopsListeningToStickyKeysWhenStickyKeySettingsIsTurnedOff() {
+        testScope.runTest {
+            collectLastValue(viewModel.indicatorContent)
+            setStickyKeysActive()
+            runCurrent()
+
+            setStickyKeySetting(enabled = false)
+            runCurrent()
+
+            verify(inputManager).unregisterStickyModifierStateListener(any())
+        }
+    }
+
     @Test
     fun stopsListeningToStickyKeysWhenKeyboardDisconnects() {
         testScope.runTest {
             collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
             runCurrent()
 
             keyboardRepository.setIsAnyKeyboardConnected(false)
@@ -111,7 +151,7 @@
     fun emitsStickyKeysListWhenStickyKeyIsPressed() {
         testScope.runTest {
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
 
             setStickyKeys(mapOf(ALT to false))
 
@@ -123,7 +163,7 @@
     fun emitsEmptyListWhenNoStickyKeysAreActive() {
         testScope.runTest {
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
 
             setStickyKeys(emptyMap())
 
@@ -135,7 +175,7 @@
     fun passesAllStickyKeysToDialog() {
         testScope.runTest {
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
 
             setStickyKeys(mapOf(
                 ALT to false,
@@ -154,7 +194,7 @@
     fun showsOnlyLockedStateIfKeyIsStickyAndLocked() {
         testScope.runTest {
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
 
             setStickyKeys(mapOf(
                 ALT to false,
@@ -168,7 +208,7 @@
     fun doesNotChangeOrderOfKeysIfTheyBecomeLocked() {
         testScope.runTest {
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
-            keyboardRepository.setIsAnyKeyboardConnected(true)
+            setStickyKeysActive()
 
             setStickyKeys(mapOf(
                 META to false,
@@ -186,6 +226,11 @@
         }
     }
 
+    private fun setStickyKeysActive() {
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+        setStickyKeySetting(enabled = true)
+    }
+
     private fun TestScope.setStickyKeys(keys: Map<ModifierKey, Boolean>) {
         runCurrent()
         verify(inputManager).registerStickyModifierStateListener(any(), captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index e9f2132..fdbba90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -27,6 +27,7 @@
 import com.android.internal.app.AssistUtils
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
@@ -109,6 +110,8 @@
     @Mock
     private lateinit var unfoldTransitionProgressForwarder:
         Optional<UnfoldTransitionProgressForwarder>
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
 
     @Before
     fun setUp() {
@@ -158,7 +161,8 @@
                 featureFlags,
                 FakeSceneContainerFlags(),
                 dumpManager,
-                unfoldTransitionProgressForwarder
+                unfoldTransitionProgressForwarder,
+                broadcastDispatcher
             )
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 5c93991..f914ed5 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -31,9 +31,12 @@
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.service.autofill.AutofillService;
+import android.service.autofill.ConvertCredentialRequest;
+import android.service.autofill.ConvertCredentialResponse;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
+import android.service.autofill.IConvertCredentialCallback;
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import android.service.autofill.SaveRequest;
@@ -69,6 +72,7 @@
     private int mPendingFillRequestId = INVALID_REQUEST_ID;
     private AtomicReference<IFillCallback> mFillCallback;
     private AtomicReference<ISaveCallback> mSaveCallback;
+    private AtomicReference<IConvertCredentialCallback> mConvertCredentialCallback;
     private final ComponentName mComponentName;
 
     private final boolean mIsCredentialAutofillService;
@@ -81,13 +85,20 @@
             extends AbstractRemoteService.VultureCallback<RemoteFillService> {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                 @NonNull String servicePackageName, int requestFlags);
+
         void onFillRequestFailure(int requestId, @Nullable CharSequence message);
+
         void onFillRequestTimeout(int requestId);
+
         void onSaveRequestSuccess(@NonNull String servicePackageName,
                 @Nullable IntentSender intentSender);
+
         // TODO(b/80093094): add timeout here too?
         void onSaveRequestFailure(@Nullable CharSequence message,
                 @NonNull String servicePackageName);
+
+        void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+                convertCredentialResponse);
     }
 
     RemoteFillService(Context context, ComponentName componentName, int userId,
@@ -211,6 +222,32 @@
         }
     }
 
+    static class IConvertCredentialCallbackDelegate extends IConvertCredentialCallback.Stub {
+
+        private WeakReference<IConvertCredentialCallback> mCallbackWeakRef;
+
+        IConvertCredentialCallbackDelegate(IConvertCredentialCallback callback) {
+            mCallbackWeakRef = new WeakReference(callback);
+        }
+
+        @Override
+        public void onSuccess(ConvertCredentialResponse convertCredentialResponse)
+                throws RemoteException {
+            IConvertCredentialCallback callback = mCallbackWeakRef.get();
+            if (callback != null) {
+                callback.onSuccess(convertCredentialResponse);
+            }
+        }
+
+        @Override
+        public void onFailure(CharSequence message) throws RemoteException {
+            IConvertCredentialCallback callback = mCallbackWeakRef.get();
+            if (callback != null) {
+                callback.onFailure(message);
+            }
+        }
+    }
+
     /**
      * Wraps an {@link IFillCallback} object using weak reference.
      *
@@ -237,6 +274,18 @@
         return callback;
     }
 
+    /**
+     * Wraps an {@link IConvertCredentialCallback} object using weak reference
+     */
+    private IConvertCredentialCallback maybeWrapWithWeakReference(
+            IConvertCredentialCallback callback) {
+        if (remoteFillServiceUseWeakReference()) {
+            mConvertCredentialCallback = new AtomicReference<>(callback);
+            return new IConvertCredentialCallbackDelegate(callback);
+        }
+        return callback;
+    }
+
     public void onFillCredentialRequest(@NonNull FillRequest request,
             IAutoFillManagerClient autofillCallback) {
         if (sVerbose) {
@@ -378,6 +427,52 @@
         }));
     }
 
+    public void onConvertCredentialRequest(
+            @NonNull ConvertCredentialRequest convertCredentialRequest) {
+        if (sVerbose) Slog.v(TAG, "calling onConvertCredentialRequest()");
+        CompletableFuture<ConvertCredentialResponse>
+                connectThenConvertCredentialRequest = postAsync(
+                    remoteService -> {
+                        if (sVerbose) {
+                            Slog.v(TAG, "calling onConvertCredentialRequest()");
+                        }
+                        CompletableFuture<ConvertCredentialResponse>
+                                convertCredentialCompletableFuture = new CompletableFuture<>();
+                        remoteService.onConvertCredentialRequest(convertCredentialRequest,
+                                maybeWrapWithWeakReference(
+                                        new IConvertCredentialCallback.Stub() {
+                                            @Override
+                                            public void onSuccess(ConvertCredentialResponse
+                                                    convertCredentialResponse) {
+                                                convertCredentialCompletableFuture
+                                                        .complete(convertCredentialResponse);
+                                            }
+
+                                            @Override
+                                            public void onFailure(CharSequence message) {
+                                                String errorMessage =
+                                                        message == null ? "" :
+                                                                    String.valueOf(message);
+                                                convertCredentialCompletableFuture
+                                                        .completeExceptionally(
+                                                            new RuntimeException(errorMessage));
+                                            }
+                                        })
+                        );
+                        return convertCredentialCompletableFuture;
+                    }).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+
+        connectThenConvertCredentialRequest.whenComplete(
+                (res, err) -> Handler.getMain().post(() -> {
+                    if (err == null) {
+                        mCallbacks.onConvertCredentialRequestSuccess(res);
+                    } else {
+                        // TODO: Add a callback function to log this failure
+                        Slog.e(TAG, "Error calling on convert credential request", err);
+                    }
+                }));
+    }
+
     public void onSaveRequest(@NonNull SaveRequest request) {
         postAsync(service -> {
             if (sVerbose) Slog.v(TAG, "calling onSaveRequest()");
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 1234703..0af703e 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.os.Bundle;
+import android.service.autofill.ConvertCredentialResponse;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
 import android.util.Slog;
@@ -98,6 +99,12 @@
 
     }
 
+    @Override
+    public void  onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+            convertCredentialResponse) {
+
+    }
+
     /**
      * Requests a new fill response.
      */
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f3b74ea..049feee 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -24,6 +24,7 @@
 import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_ONLY;
 import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
 import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
+import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -44,6 +45,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 import static android.view.autofill.AutofillManager.COMMIT_REASON_SESSION_DESTROYED;
 import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
+import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -130,6 +132,7 @@
 import android.service.autofill.AutofillFieldClassificationService.Scores;
 import android.service.autofill.AutofillService;
 import android.service.autofill.CompositeUserData;
+import android.service.autofill.ConvertCredentialResponse;
 import android.service.autofill.Dataset;
 import android.service.autofill.Dataset.DatasetEligibleReason;
 import android.service.autofill.Field;
@@ -2429,6 +2432,29 @@
         removeFromService();
     }
 
+    // FillServiceCallbacks
+    @Override
+    public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
+            convertCredentialResponse) {
+        Dataset dataset = convertCredentialResponse.getDataset();
+        Bundle clientState = convertCredentialResponse.getClientState();
+        if (dataset != null) {
+            int requestId = -1;
+            if (clientState != null) {
+                requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID);
+            } else {
+                Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this "
+                        + "would cause loss in logging.");
+            }
+            // TODO: Add autofill related logging; consider whether to log the index
+            fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
+        } else {
+            // TODO: Add logging to log this error case
+            Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is "
+                    + "null");
+        }
+    }
+
     /**
      * Gets the {@link FillContext} for a request.
      *
diff --git a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
index 3482863..aac628c 100644
--- a/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
+++ b/services/companion/java/com/android/server/companion/InactiveAssociationsRemovalService.java
@@ -36,7 +36,8 @@
  * will be killed if association/role are revoked.
  */
 public class InactiveAssociationsRemovalService extends JobService {
-    private static final int JOB_ID = InactiveAssociationsRemovalService.class.hashCode();
+    private static final String JOB_NAMESPACE = "companion";
+    private static final int JOB_ID = 1;
     private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1);
 
     @Override
@@ -61,7 +62,8 @@
 
     static void schedule(Context context) {
         Slog.i(TAG, "Scheduling the Association Removal job");
-        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        final JobScheduler jobScheduler =
+                context.getSystemService(JobScheduler.class).forNamespace(JOB_NAMESPACE);
         final JobInfo job = new JobInfo.Builder(JOB_ID,
                 new ComponentName(context, InactiveAssociationsRemovalService.class))
                 .setRequiresCharging(true)
@@ -71,4 +73,3 @@
         jobScheduler.schedule(job);
     }
 }
-
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a54a48a..8e35b74 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -139,6 +139,7 @@
 
     libs: [
         "services.net",
+        "android.frameworks.location.altitude-V2-java",
         "android.hardware.common-V2-java",
         "android.hardware.light-V2.0-java",
         "android.hardware.gnss-V2-java",
@@ -160,7 +161,6 @@
     ],
 
     static_libs: [
-        "android.frameworks.location.altitude-V2-java", // AIDL
         "android.frameworks.vibrator-V1-java", // AIDL
         "android.hardware.authsecret-V1.0-java",
         "android.hardware.authsecret-V1-java",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index afb8345..adc0255 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -181,7 +181,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.ServiceInfo.ForegroundServiceType;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -306,6 +305,57 @@
     @interface FgsStopReason {}
 
     /**
+     * The policy to be applied to the service bindings; this one means it follows the legacy
+     * behavior.
+     */
+    static final int SERVICE_BIND_OOMADJ_POLICY_LEGACY = 0;
+
+    /**
+     * The policy to be applied to the service bindings; this one means we'll skip
+     * updating the target process's oom adj score / process state for its {@link Service#onCreate}.
+     */
+    static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE = 1;
+
+    /**
+     * The policy to be applied to the service bindings; this one means we'll skip
+     * updating the target process's oom adj score / process state for its {@link Service#onBind}.
+     */
+    static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND = 1 << 1;
+
+    /**
+     * The policy to be applied to the service bindings; this one means we'll skip
+     * updating the target process's oom adj score / process state on setting up the service
+     * connection between the client and the service host process.
+     */
+    static final int SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT = 1 << 2;
+    /**
+     * The policy to be applied to the service bindings; this one means the caller
+     * will be frozen upon calling the bindService APIs.
+     */
+    static final int SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER = 1 << 3;
+
+    @IntDef(flag = true, prefix = { "SERVICE_BIND_OOMADJ_POLICY_" }, value = {
+            SERVICE_BIND_OOMADJ_POLICY_LEGACY,
+            SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE,
+            SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND,
+            SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT,
+            SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ServiceBindingOomAdjPolicy {}
+
+    @ServiceBindingOomAdjPolicy
+    static final int DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG =
+            SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE
+            | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND
+            | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT;
+
+    @ServiceBindingOomAdjPolicy
+    static final int DEFAULT_SERVICE_CACHED_BIND_POLICY_FLAG =
+            SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE
+            | SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND;
+
+    /**
      * Disables foreground service background starts from BOOT_COMPLETED broadcasts for all types
      * except:
      * <ul>
@@ -1244,7 +1294,7 @@
                             @Override
                             public void onResult(Bundle result) {
                                 synchronized (mAm) {
-                                    final long identity = Binder.clearCallingIdentity();
+                                    final long identity = mAm.mInjector.clearCallingIdentity();
                                     try {
                                         if (!mPendingServices.contains(r)) {
                                             return;
@@ -1263,7 +1313,8 @@
                                                         false /* whileRestarting */,
                                                         false /* permissionsReviewRequired */,
                                                         false /* packageFrozen */,
-                                                        true /* enqueueOomAdj */);
+                                                        true /* enqueueOomAdj */,
+                                                        SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                                             } catch (RemoteException e) {
                                                 /* ignore - local call */
                                             } finally {
@@ -1275,7 +1326,7 @@
                                             unbindServiceLocked(connection);
                                         }
                                     } finally {
-                                        Binder.restoreCallingIdentity(identity);
+                                        mAm.mInjector.restoreCallingIdentity(identity);
                                     }
                                 }
                             }
@@ -1353,7 +1404,8 @@
                                     false /* whileRestarting */,
                                     false /* permissionsReviewRequired */,
                                     false /* packageFrozen */,
-                                    true /* enqueueOomAdj */);
+                                    true /* enqueueOomAdj */,
+                                    SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                         } catch (TransactionTooLargeException e) {
                             /* ignore - local call */
                         } finally {
@@ -1431,7 +1483,8 @@
                 false /* whileRestarting */,
                 false /* permissionsReviewRequired */,
                 false /* packageFrozen */,
-                true /* enqueueOomAdj */);
+                true /* enqueueOomAdj */,
+                SERVICE_BIND_OOMADJ_POLICY_LEGACY);
         /* Will be a no-op if nothing pending */
         mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
         if (error != null) {
@@ -1550,22 +1603,22 @@
         if (caller != null && callerApp == null) {
             throw new SecurityException(
                     "Unable to find app for caller " + caller
-                    + " (pid=" + Binder.getCallingPid()
+                    + " (pid=" + mAm.mInjector.getCallingPid()
                     + ") when stopping service " + service);
         }
 
         // If this service is active, make sure it is stopped.
         ServiceLookupResult r = retrieveServiceLocked(service, instanceName, isSdkSandboxService,
                 sdkSandboxClientAppUid, sdkSandboxClientAppPackage, resolvedType, null,
-                Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false,
-                null, false, false);
+                mAm.mInjector.getCallingPid(), mAm.mInjector.getCallingUid(),
+                userId, false, false, false, false, null, false, false);
         if (r != null) {
             if (r.record != null) {
-                final long origId = Binder.clearCallingIdentity();
+                final long origId = mAm.mInjector.clearCallingIdentity();
                 try {
                     stopServiceLocked(r.record, false);
                 } finally {
-                    Binder.restoreCallingIdentity(origId);
+                    mAm.mInjector.restoreCallingIdentity(origId);
                 }
                 return 1;
             }
@@ -1649,7 +1702,7 @@
 
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
         ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
-                Binder.getCallingPid(), Binder.getCallingUid(),
+                mAm.mInjector.getCallingPid(), mAm.mInjector.getCallingUid(),
                 UserHandle.getCallingUserId(), false, false, false, false, false, false);
 
         IBinder ret = null;
@@ -1658,8 +1711,8 @@
             if (r.record == null) {
                 throw new SecurityException(
                         "Permission Denial: Accessing service"
-                        + " from pid=" + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid()
+                        + " from pid=" + mAm.mInjector.getCallingPid()
+                        + ", uid=" + mAm.mInjector.getCallingUid()
                         + " requires " + r.permission);
             }
             IntentBindRecord ib = r.record.bindings.get(r.record.intent);
@@ -1719,9 +1772,9 @@
                 }
             }
             r.callStart = false;
-            final long origId = Binder.clearCallingIdentity();
+            final long origId = mAm.mInjector.clearCallingIdentity();
             bringDownServiceIfNeededLocked(r, false, false, false, "stopServiceToken");
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
             return true;
         }
         return false;
@@ -1734,14 +1787,14 @@
     public void setServiceForegroundLocked(ComponentName className, IBinder token,
             int id, Notification notification, int flags, int foregroundServiceType) {
         final int userId = UserHandle.getCallingUserId();
-        final long origId = Binder.clearCallingIdentity();
+        final long origId = mAm.mInjector.clearCallingIdentity();
         try {
             ServiceRecord r = findServiceLocked(className, token, userId);
             if (r != null) {
                 setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);
             }
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
     }
 
@@ -1753,7 +1806,7 @@
      */
     public int getForegroundServiceTypeLocked(ComponentName className, IBinder token) {
         final int userId = UserHandle.getCallingUserId();
-        final long origId = Binder.clearCallingIdentity();
+        final long origId = mAm.mInjector.clearCallingIdentity();
         int ret = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
         try {
             ServiceRecord r = findServiceLocked(className, token, userId);
@@ -1761,7 +1814,7 @@
                 ret = r.foregroundServiceType;
             }
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
         return ret;
     }
@@ -3483,7 +3536,7 @@
 
     boolean shouldServiceTimeOutLocked(ComponentName className, IBinder token) {
         final int userId = UserHandle.getCallingUserId();
-        final long ident = Binder.clearCallingIdentity();
+        final long ident = mAm.mInjector.clearCallingIdentity();
         try {
             ServiceRecord sr = findServiceLocked(className, token, userId);
             if (sr == null) {
@@ -3492,7 +3545,7 @@
             final long nowUptime = SystemClock.uptimeMillis();
             return sr.shouldTriggerShortFgsTimeout(nowUptime);
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mAm.mInjector.restoreCallingIdentity(ident);
         }
     }
 
@@ -3636,8 +3689,8 @@
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                 + " type=" + resolvedType + " conn=" + connection.asBinder()
                 + " flags=0x" + Long.toHexString(flags));
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
+        final int callingPid = mAm.mInjector.getCallingPid();
+        final int callingUid = mAm.mInjector.getCallingUid();
         final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
         if (callerApp == null) {
             throw new SecurityException(
@@ -3778,7 +3831,7 @@
                 && !requestStartTargetPermissionsReviewIfNeededLocked(s, callingPackage, null,
                         callingUid, service, callerFg, userId, true, connection);
 
-        final long origId = Binder.clearCallingIdentity();
+        final long origId = mAm.mInjector.clearCallingIdentity();
 
         try {
             if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
@@ -3859,12 +3912,34 @@
             }
             clist.add(c);
 
+            final boolean isolated = (s.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
+            final ProcessRecord hostApp = isolated
+                    ? null
+                    : mAm.getProcessRecordLocked(s.processName, s.appInfo.uid);
+            final int serviceBindingOomAdjPolicy = hostApp != null
+                    ? getServiceBindingOomAdjPolicyForAddLocked(b.client, hostApp, c)
+                    : SERVICE_BIND_OOMADJ_POLICY_LEGACY;
+
+            final boolean shouldFreezeCaller = !packageFrozen && !permissionsReviewRequired
+                    && (serviceBindingOomAdjPolicy & SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER) != 0
+                    && callerApp.isFreezable();
+
+            if (shouldFreezeCaller) {
+                // Freeze the caller immediately, so the following #onBind/#onConnected will be
+                // queued up in the app side as they're one way calls. And we'll also hold off
+                // the service timeout timer until the process is unfrozen.
+                mAm.mOomAdjuster.updateAppFreezeStateLSP(callerApp, OOM_ADJ_REASON_BIND_SERVICE,
+                        true);
+            }
+
             boolean needOomAdj = false;
             if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
                 s.lastActivity = SystemClock.uptimeMillis();
-                needOomAdj = true;
+                needOomAdj = (serviceBindingOomAdjPolicy
+                        & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) == 0;
                 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
-                        permissionsReviewRequired, packageFrozen, true) != null) {
+                        permissionsReviewRequired, packageFrozen, true, serviceBindingOomAdjPolicy)
+                        != null) {
                     mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_BIND_SERVICE);
                     return 0;
                 }
@@ -3886,8 +3961,11 @@
                         || (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP
                             && c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)),
                         b.client);
-                needOomAdj = true;
-                mAm.enqueueOomAdjTargetLocked(s.app);
+                if ((serviceBindingOomAdjPolicy
+                        & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) {
+                    needOomAdj = true;
+                    mAm.enqueueOomAdjTargetLocked(s.app);
+                }
             }
             if (needOomAdj) {
                 mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_BIND_SERVICE);
@@ -3937,10 +4015,12 @@
                 // and the service had previously asked to be told when
                 // rebound, then do so.
                 if (b.intent.apps.size() == 1 && b.intent.doRebind) {
-                    requestServiceBindingLocked(s, b.intent, callerFg, true);
+                    requestServiceBindingLocked(s, b.intent, callerFg, true,
+                            serviceBindingOomAdjPolicy);
                 }
             } else if (!b.intent.requested) {
-                requestServiceBindingLocked(s, b.intent, callerFg, false);
+                requestServiceBindingLocked(s, b.intent, callerFg, false,
+                        serviceBindingOomAdjPolicy);
             }
 
             maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);
@@ -3948,7 +4028,7 @@
             getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
 
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
 
         notifyBindingServiceEventLocked(callerApp, callingPackage);
@@ -3982,7 +4062,7 @@
     }
 
     void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
-        final long origId = Binder.clearCallingIdentity();
+        final long origId = mAm.mInjector.clearCallingIdentity();
         try {
             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                     + " " + intent + ": " + service);
@@ -4025,10 +4105,11 @@
                 }
 
                 serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false,
-                        OOM_ADJ_REASON_EXECUTING_SERVICE);
+                        !Flags.serviceBindingOomAdjPolicy() || b == null || !b.mSkippedOomAdj
+                        ? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE);
             }
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
     }
 
@@ -4078,8 +4159,8 @@
             return false;
         }
 
-        final int callingPid = Binder.getCallingPid();
-        final long origId = Binder.clearCallingIdentity();
+        final int callingPid = mAm.mInjector.getCallingPid();
+        final long origId = mAm.mInjector.clearCallingIdentity();
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                 String info;
@@ -4092,9 +4173,10 @@
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "unbindServiceLocked: " + info);
             }
 
+            boolean needOomAdj = false;
             while (clist.size() > 0) {
                 ConnectionRecord r = clist.get(0);
-                removeConnectionLocked(r, null, null, true);
+                int serviceBindingOomAdjPolicy = removeConnectionLocked(r, null, null, true);
                 if (clist.size() > 0 && clist.get(0) == r) {
                     // In case it didn't get removed above, do it now.
                     Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
@@ -4112,22 +4194,28 @@
                         psr.setTreatLikeActivity(true);
                         mAm.updateLruProcessLocked(app, true, null);
                     }
-                    mAm.enqueueOomAdjTargetLocked(app);
+                    // If the bindee is more important than the binder, we may skip the OomAdjuster.
+                    if (serviceBindingOomAdjPolicy == SERVICE_BIND_OOMADJ_POLICY_LEGACY) {
+                        mAm.enqueueOomAdjTargetLocked(app);
+                        needOomAdj = true;
+                    }
                 }
             }
 
-            mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_UNBIND_SERVICE);
+            if (needOomAdj) {
+                mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_UNBIND_SERVICE);
+            }
 
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
 
         return true;
     }
 
     void unbindFinishedLocked(ServiceRecord r, Intent intent, boolean doRebind) {
-        final long origId = Binder.clearCallingIdentity();
+        final long origId = mAm.mInjector.clearCallingIdentity();
         try {
             if (r != null) {
                 Intent.FilterComparison filter
@@ -4138,6 +4226,7 @@
                         + (b != null ? b.apps.size() : 0));
 
                 boolean inDestroying = mDestroyingServices.contains(r);
+                boolean skipOomAdj = false;
                 if (b != null) {
                     if (b.apps.size() > 0 && !inDestroying) {
                         // Applications have already bound since the last
@@ -4152,7 +4241,8 @@
                             }
                         }
                         try {
-                            requestServiceBindingLocked(r, b, inFg, true);
+                            requestServiceBindingLocked(r, b, inFg, true,
+                                    SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                         } catch (TransactionTooLargeException e) {
                             // Don't pass this back to ActivityThread, it's unrelated.
                         }
@@ -4161,13 +4251,14 @@
                         // a new client.
                         b.doRebind = true;
                     }
+                    skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b.mSkippedOomAdj;
                 }
 
                 serviceDoneExecutingLocked(r, inDestroying, false, false,
-                        OOM_ADJ_REASON_UNBIND_SERVICE);
+                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
             }
         } finally {
-            Binder.restoreCallingIdentity(origId);
+            mAm.mInjector.restoreCallingIdentity(origId);
         }
     }
 
@@ -4503,7 +4594,7 @@
                         userId = 0;
                         smap = getServiceMapLocked(0);
                         // Bypass INTERACT_ACROSS_USERS permission check
-                        final long token = Binder.clearCallingIdentity();
+                        final long token = mAm.mInjector.clearCallingIdentity();
                         try {
                             ResolveInfo rInfoForUserId0 =
                                     mAm.getPackageManagerInternal().resolveService(service,
@@ -4516,7 +4607,7 @@
                             }
                             sInfo = rInfoForUserId0.serviceInfo;
                         } finally {
-                            Binder.restoreCallingIdentity(token);
+                            mAm.mInjector.restoreCallingIdentity(token);
                         }
                     }
                     sInfo = new ServiceInfo(sInfo);
@@ -4645,7 +4736,8 @@
      * @return {@code true} if it performed oomAdjUpdate.
      */
     private boolean bumpServiceExecutingLocked(
-            ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason) {
+            ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason,
+            boolean skipTimeoutIfPossible) {
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
                 + why + " of " + r + " in app " + r.app);
         else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
@@ -4669,6 +4761,10 @@
             timeoutNeeded = false;
         }
 
+        // If the process is frozen or to be frozen, and we want to skip the timeout, skip it.
+        final boolean shouldSkipTimeout = skipTimeoutIfPossible && r.app != null
+                && (r.app.mOptRecord.isPendingFreeze() || r.app.mOptRecord.isFrozen());
+
         ProcessServiceRecord psr;
         if (r.executeNesting == 0) {
             r.executeFg = fg;
@@ -4684,7 +4780,11 @@
                 psr.startExecutingService(r);
                 psr.setExecServicesFg(psr.shouldExecServicesFg() || fg);
                 if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
-                    scheduleServiceTimeoutLocked(r.app);
+                    if (!shouldSkipTimeout) {
+                        scheduleServiceTimeoutLocked(r.app);
+                    } else {
+                        r.app.mServices.noteScheduleServiceTimeoutPending(true);
+                    }
                 }
             }
         } else if (r.app != null && fg) {
@@ -4692,7 +4792,11 @@
             if (!psr.shouldExecServicesFg()) {
                 psr.setExecServicesFg(true);
                 if (timeoutNeeded) {
-                    scheduleServiceTimeoutLocked(r.app);
+                    if (!shouldSkipTimeout) {
+                        scheduleServiceTimeoutLocked(r.app);
+                    } else {
+                        r.app.mServices.noteScheduleServiceTimeoutPending(true);
+                    }
                 }
             }
         }
@@ -4712,16 +4816,22 @@
     }
 
     private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
-            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
+            boolean execInFg, boolean rebind,
+            @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
+            throws TransactionTooLargeException {
         if (r.app == null || r.app.getThread() == null) {
             // If service is not currently running, can't yet bind.
             return false;
         }
         if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                 + " rebind=" + rebind);
+        final boolean skipOomAdj = (serviceBindingOomAdjPolicy
+                & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND) != 0;
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
-                bumpServiceExecutingLocked(r, execInFg, "bind", OOM_ADJ_REASON_BIND_SERVICE);
+                i.mSkippedOomAdj = !bumpServiceExecutingLocked(r, execInFg, "bind",
+                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_BIND_SERVICE,
+                        skipOomAdj /* skipTimeoutIfPossible */);
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                     Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "requestServiceBinding="
                             + i.intent.getIntent() + ". bindSeq=" + mBindServiceSeqCounter);
@@ -4738,14 +4848,14 @@
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        OOM_ADJ_REASON_UNBIND_SERVICE);
+                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
                 throw e;
             } catch (RemoteException e) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                 // Keep the executeNesting count accurate.
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        OOM_ADJ_REASON_UNBIND_SERVICE);
+                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
                 return false;
             }
         }
@@ -5117,7 +5227,7 @@
         }
         try {
             bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false,
-                    false, true);
+                    false, true, SERVICE_BIND_OOMADJ_POLICY_LEGACY);
         } catch (TransactionTooLargeException e) {
             // Ignore, it's been logged and nothing upstack cares.
         } finally {
@@ -5217,7 +5327,7 @@
 
     private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
             boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
-            boolean enqueueOomAdj)
+            boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
             throws TransactionTooLargeException {
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -5225,7 +5335,8 @@
                         "bringUpServiceLocked: " + r.shortInstanceName);
             }
             return bringUpServiceInnerLocked(r, intentFlags, execInFg, whileRestarting,
-                    permissionsReviewRequired, packageFrozen, enqueueOomAdj);
+                    permissionsReviewRequired, packageFrozen, enqueueOomAdj,
+                    serviceBindingOomAdjPolicy);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
@@ -5233,7 +5344,7 @@
 
     private String bringUpServiceInnerLocked(ServiceRecord r, int intentFlags, boolean execInFg,
             boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
-            boolean enqueueOomAdj)
+            boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
             throws TransactionTooLargeException {
         if (r.app != null && r.app.isThreadReady()) {
             sendServiceArgsLocked(r, execInFg, false);
@@ -5317,7 +5428,7 @@
                         app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                                 mAm.mProcessStats);
                         realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
-                                enqueueOomAdj);
+                                enqueueOomAdj, serviceBindingOomAdjPolicy);
                         return null;
                     } catch (TransactionTooLargeException e) {
                         throw e;
@@ -5347,7 +5458,7 @@
                                         "realStartServiceLocked: " + r.shortInstanceName);
                             }
                             realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
-                                    enqueueOomAdj);
+                                    enqueueOomAdj, SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                             return null;
                         } catch (TransactionTooLargeException e) {
                             throw e;
@@ -5452,16 +5563,61 @@
         return HostingRecord.TRIGGER_TYPE_UNKNOWN;
     }
 
-    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
+    private void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg,
+            @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
             throws TransactionTooLargeException {
         for (int i=r.bindings.size()-1; i>=0; i--) {
             IntentBindRecord ibr = r.bindings.valueAt(i);
-            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
+            if (!requestServiceBindingLocked(r, ibr, execInFg, false, serviceBindingOomAdjPolicy)) {
                 break;
             }
         }
     }
 
+    @ServiceBindingOomAdjPolicy
+    private int getServiceBindingOomAdjPolicyForAddLocked(ProcessRecord clientApp,
+            ProcessRecord hostApp, ConnectionRecord cr) {
+        @ServiceBindingOomAdjPolicy int policy = SERVICE_BIND_OOMADJ_POLICY_LEGACY;
+        if (Flags.serviceBindingOomAdjPolicy() && clientApp != null && hostApp != null) {
+            if (clientApp == hostApp) {
+                policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+            } else if (clientApp.isCached()) {
+                policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+                if (clientApp.isFreezable()) {
+                    policy |= SERVICE_BIND_OOMADJ_POLICY_FREEZE_CALLER;
+                }
+            }
+            if ((policy & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) {
+                // Binding between two different processes.
+                // Check if the caller has a better process state, oom adj score,
+                // or if the caller has more capabilities.
+                if (!mAm.mOomAdjuster.evaluateServiceConnectionAdd(clientApp, hostApp, cr)) {
+                    // Running an oom adjuster won't be give the host app a better score, skip it.
+                    policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+                }
+            }
+        }
+        return policy;
+    }
+
+    @ServiceBindingOomAdjPolicy
+    private int getServiceBindingOomAdjPolicyForRemovalLocked(ProcessRecord clientApp,
+            ProcessRecord hostApp, ConnectionRecord cr) {
+        @ServiceBindingOomAdjPolicy int policy = SERVICE_BIND_OOMADJ_POLICY_LEGACY;
+        if (Flags.serviceBindingOomAdjPolicy() && clientApp != null && hostApp != null
+                && cr != null) {
+            if (clientApp == hostApp) {
+                policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+            } else {
+                if (!mAm.mOomAdjuster.evaluateServiceConnectionRemoval(clientApp, hostApp, cr)) {
+                    // Running an oom adjuster won't be give the host app a better score, skip it.
+                    policy = DEFAULT_SERVICE_NO_BUMP_BIND_POLICY_FLAG;
+                }
+            }
+        }
+        return policy;
+    }
+
     /**
      * Note the name of this method should not be confused with the started services concept.
      * The "start" here means bring up the instance in the client, and this method is called
@@ -5469,7 +5625,8 @@
      */
     private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
             IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
-            boolean enqueueOomAdj) throws RemoteException {
+            boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
+            throws RemoteException {
         if (thread == null) {
             throw new RemoteException();
         }
@@ -5478,17 +5635,28 @@
                     + ", ProcessRecord.uid = " + app.uid);
         r.setProcess(app, thread, pid, uidRecord);
         r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
-
+        final boolean skipOomAdj = (serviceBindingOomAdjPolicy
+                & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) != 0;
         final ProcessServiceRecord psr = app.mServices;
         final boolean newService = psr.startService(r);
         bumpServiceExecutingLocked(r, execInFg, "create",
-                OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */);
+                OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */,
+                skipOomAdj /* skipTimeoutIfPossible */);
         mAm.updateLruProcessLocked(app, false, null);
         updateServiceForegroundLocked(psr, /* oomAdj= */ false);
-        // Force an immediate oomAdjUpdate, so the client app could be in the correct process state
-        // before doing any service related transactions
-        mAm.enqueueOomAdjTargetLocked(app);
-        mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
+        // Skip the oom adj update if it's a self-binding, the Service#onCreate() will be running
+        // at its current adj score.
+        if (!skipOomAdj) {
+            // Force an immediate oomAdjUpdate, so the host app could be in the correct
+            // process state before doing any service related transactions
+            mAm.enqueueOomAdjTargetLocked(app);
+            mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
+        } else {
+            // Since we skipped the oom adj update, the Service#onCreate() might be running in
+            // the cached state, if the service process drops into the cached state after the call.
+            // But there is still a grace period before freezing it, so we should be fine
+            // in terms of not getting an ANR.
+        }
 
         boolean created = false;
         try {
@@ -5523,7 +5691,7 @@
                 // Keep the executeNesting count accurate.
                 final boolean inDestroying = mDestroyingServices.contains(r);
                 serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
-                        OOM_ADJ_REASON_STOP_SERVICE);
+                        skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_STOP_SERVICE);
 
                 // Cleanup.
                 if (newService) {
@@ -5542,7 +5710,7 @@
             psr.mAllowlistManager = true;
         }
 
-        requestServiceBindingsLocked(r, execInFg);
+        requestServiceBindingsLocked(r, execInFg, serviceBindingOomAdjPolicy);
 
         updateServiceClientActivitiesLocked(psr, null, true);
 
@@ -5610,7 +5778,8 @@
                     UserHandle.getAppId(r.appInfo.uid)
             );
             bumpServiceExecutingLocked(r, execInFg, "start",
-                    OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */);
+                    OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */,
+                    false /* skipTimeoutIfPossible */);
             if (r.fgRequired && !r.fgWaiting) {
                 if (!r.isForeground) {
                     if (DEBUG_BACKGROUND_CHECK) {
@@ -5753,7 +5922,8 @@
                 if (ibr.hasBound) {
                     try {
                         oomAdjusted |= bumpServiceExecutingLocked(r, false, "bring down unbind",
-                                OOM_ADJ_REASON_UNBIND_SERVICE);
+                                OOM_ADJ_REASON_UNBIND_SERVICE,
+                                false /* skipTimeoutIfPossible */);
                         ibr.hasBound = false;
                         ibr.requested = false;
                         r.app.getThread().scheduleUnbindService(r,
@@ -5909,7 +6079,8 @@
                 } else {
                     try {
                         oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
-                                oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE);
+                                oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE,
+                                false /* skipTimeoutIfPossible */);
                         mDestroyingServices.add(r);
                         r.destroying = true;
                         r.app.getThread().scheduleStopService(r);
@@ -5992,11 +6163,17 @@
         }
     }
 
-    void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
+    /**
+     * @return The ServiceBindingOomAdjPolicy used in this removal.
+     */
+    @ServiceBindingOomAdjPolicy
+    int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
             ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
         ServiceRecord s = b.service;
+        @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy =
+                SERVICE_BIND_OOMADJ_POLICY_LEGACY;
         ArrayList<ConnectionRecord> clist = s.getConnections().get(binder);
         if (clist != null) {
             clist.remove(c);
@@ -6055,8 +6232,14 @@
                     + ": shouldUnbind=" + b.intent.hasBound);
             if (s.app != null && s.app.isThreadReady() && b.intent.apps.size() == 0
                     && b.intent.hasBound) {
+                serviceBindingOomAdjPolicy = getServiceBindingOomAdjPolicyForRemovalLocked(b.client,
+                        s.app, c);
+                final boolean skipOomAdj = (serviceBindingOomAdjPolicy
+                        & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) != 0;
                 try {
-                    bumpServiceExecutingLocked(s, false, "unbind", OOM_ADJ_REASON_UNBIND_SERVICE);
+                    b.intent.mSkippedOomAdj = !bumpServiceExecutingLocked(s, false, "unbind",
+                            skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
+                            skipOomAdj /* skipTimeoutIfPossible */);
                     if (b.client != s.app && c.notHasFlag(Context.BIND_WAIVE_PRIORITY)
                             && s.app.mState.getSetProcState() <= PROCESS_STATE_HEAVY_WEIGHT) {
                         // If this service's process is not already in the cached list,
@@ -6096,12 +6279,14 @@
                         "removeConnection");
             }
         }
+        return serviceBindingOomAdjPolicy;
     }
 
     void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
-            boolean enqueueOomAdj) {
+            boolean enqueueOomAdj, Intent intent) {
         boolean inDestroying = mDestroyingServices.contains(r);
         if (r != null) {
+            boolean skipOomAdj = false;
             if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
                 // This is a call from a service start...  take care of
                 // book-keeping.
@@ -6177,14 +6362,19 @@
                     // Fake it to keep from ANR due to orphaned entry.
                     r.executeNesting = 1;
                 }
+            } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_REBIND
+                    || type == ActivityThread.SERVICE_DONE_EXECUTING_UNBIND) {
+                final Intent.FilterComparison filter = new Intent.FilterComparison(intent);
+                final IntentBindRecord b = r.bindings.get(filter);
+                skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b != null && b.mSkippedOomAdj;
             }
-            final long origId = Binder.clearCallingIdentity();
+            final long origId = mAm.mInjector.clearCallingIdentity();
             serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj,
-                    OOM_ADJ_REASON_EXECUTING_SERVICE);
-            Binder.restoreCallingIdentity(origId);
+                    skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_EXECUTING_SERVICE);
+            mAm.mInjector.restoreCallingIdentity(origId);
         } else {
             Slog.w(TAG, "Done executing unknown service from pid "
-                    + Binder.getCallingPid());
+                    + mAm.mInjector.getCallingPid());
         }
     }
 
@@ -6236,10 +6426,17 @@
                     mDestroyingServices.remove(r);
                     r.bindings.clear();
                 }
-                if (enqueueOomAdj) {
-                    mAm.enqueueOomAdjTargetLocked(r.app);
+                boolean oomAdjusted = false;
+                if (oomAdjReason != OOM_ADJ_REASON_NONE) {
+                    if (enqueueOomAdj) {
+                        mAm.enqueueOomAdjTargetLocked(r.app);
+                    } else {
+                        mAm.updateOomAdjLocked(r.app, oomAdjReason);
+                    }
+                    oomAdjusted = true;
                 } else {
-                    mAm.updateOomAdjLocked(r.app, oomAdjReason);
+                    // Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked()
+                    oomAdjusted = false;
                 }
             }
             r.executeFg = false;
@@ -6296,7 +6493,7 @@
                                     "realStartServiceLocked: " + sr.shortInstanceName);
                         }
                         realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
-                                true);
+                                true, SERVICE_BIND_OOMADJ_POLICY_LEGACY);
                     } finally {
                         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     }
@@ -6786,6 +6983,7 @@
         }
 
         psr.stopAllExecutingServices();
+        psr.noteScheduleServiceTimeoutPending(false);
     }
 
     ActivityManager.RunningServiceInfo makeRunningServiceInfoLocked(ServiceRecord r) {
@@ -6836,7 +7034,7 @@
         ArrayList<ActivityManager.RunningServiceInfo> res
                 = new ArrayList<ActivityManager.RunningServiceInfo>();
 
-        final long ident = Binder.clearCallingIdentity();
+        final long ident = mAm.mInjector.clearCallingIdentity();
         try {
             if (canInteractAcrossUsers) {
                 int[] users = mAm.mUserController.getUsers();
@@ -6878,14 +7076,14 @@
                 }
             }
         } finally {
-            Binder.restoreCallingIdentity(ident);
+            mAm.mInjector.restoreCallingIdentity(ident);
         }
 
         return res;
     }
 
     public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) {
-        int userId = UserHandle.getUserId(Binder.getCallingUid());
+        int userId = UserHandle.getUserId(mAm.mInjector.getCallingUid());
         ServiceRecord r = getServiceByNameLocked(name, userId);
         if (r != null) {
             ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
@@ -7077,6 +7275,7 @@
         final long delay = proc.mServices.shouldExecServicesFg()
                 ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT;
         mActiveServiceAnrTimer.start(proc, delay);
+        proc.mServices.noteScheduleServiceTimeoutPending(false);
     }
 
     void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca04e41..74902f7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1751,7 +1751,7 @@
     @GuardedBy("mProcLock")
     private long mLastBinderHeavyHitterAutoSamplerStart = 0L;
 
-    final AppProfiler mAppProfiler;
+    AppProfiler mAppProfiler;
 
     private static final int INDEX_NATIVE_PSS = 0;
     private static final int INDEX_NATIVE_SWAP_PSS = 1;
@@ -2496,7 +2496,7 @@
         mInjector = injector;
         mContext = mInjector.getContext();
         mUiContext = null;
-        mAppErrors = null;
+        mAppErrors = injector.getAppErrors();
         mPackageWatchdog = null;
         mAppOpsService = mInjector.getAppOpsService(null /* recentAccessesFile */,
             null /* storageFile */, null /* handler */);
@@ -2514,7 +2514,7 @@
                 ? new OomAdjusterModernImpl(this, mProcessList, activeUids, handlerThread)
                 : new OomAdjuster(this, mProcessList, activeUids, handlerThread);
 
-        mIntentFirewall = null;
+        mIntentFirewall = injector.getIntentFirewall();
         mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
         mCpHelper = new ContentProviderHelper(this, false);
         mServices = mInjector.getActiveServices(this);
@@ -13889,13 +13889,15 @@
         }
     }
 
-    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
+    @Override
+    public void serviceDoneExecuting(IBinder token, int type, int startId, int res, Intent intent) {
         synchronized(this) {
             if (!(token instanceof ServiceRecord)) {
                 Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                 throw new IllegalArgumentException("Invalid service token");
             }
-            mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
+            mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false,
+                    intent);
         }
     }
 
@@ -20236,6 +20238,36 @@
             }
             return broadcastQueues;
         }
+
+        /** @see Binder#getCallingUid */
+        public int getCallingUid() {
+            return Binder.getCallingUid();
+        }
+
+        /** @see Binder#getCallingPid */
+        public int getCallingPid() {
+            return Binder.getCallingUid();
+        }
+
+        /** @see Binder#clearCallingIdentity */
+        public long clearCallingIdentity() {
+            return Binder.clearCallingIdentity();
+        }
+
+        /** @see Binder#clearCallingIdentity */
+        public void restoreCallingIdentity(long ident) {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        /** @return the default instance of AppErrors */
+        public AppErrors getAppErrors() {
+            return null;
+        }
+
+        /** @return the default instance of intent firewall */
+        public IntentFirewall getIntentFirewall() {
+            return null;
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 626b70b..d92a24b 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1419,6 +1419,11 @@
     }
 
     @GuardedBy({"mAm", "mProcLock"})
+    void freezeAppAsyncAtEarliestLSP(ProcessRecord app) {
+        freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, 0));
+    }
+
+    @GuardedBy({"mAm", "mProcLock"})
     void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis,
             boolean force) {
         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
@@ -1714,6 +1719,14 @@
                 compactApp(frozenProc, CompactProfile.FULL, CompactSource.APP, false);
             }
         }
+        frozenProc.onProcessFrozen();
+    }
+
+    /**
+     * Callback received when an attempt to freeze a process is cancelled (failed).
+     */
+    void onProcessFrozenCancelled(ProcessRecord app) {
+        app.onProcessFrozenCancelled();
     }
 
     /**
@@ -2203,6 +2216,8 @@
                         onProcessFrozen(proc);
                         removeMessages(DEADLOCK_WATCHDOG_MSG);
                         sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);
+                    } else {
+                        onProcessFrozenCancelled(proc);
                     }
                 } break;
                 case REPORT_UNFREEZE_MSG: {
@@ -2460,7 +2475,7 @@
                                 pr = mAm.mPidsSelfLocked.get(blocked);
                             }
                             if (pr != null
-                                    && pr.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
+                                    && pr.mState.getCurAdj() < ProcessList.FREEZER_CUTOFF_ADJ) {
                                 Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
                                         + pr.processName + " (" + blocked + ")");
                                 // Found at least one blocked non-cached process
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 30f21a6..cb7898d 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -35,6 +35,7 @@
 import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerService.TAG_MU;
+import static com.android.server.am.Flags.serviceBindingOomAdjPolicy;
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -319,8 +320,10 @@
 
                     checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
-                    boolean success = mService.updateOomAdjLocked(cpr.proc,
-                            OOM_ADJ_REASON_GET_PROVIDER);
+                    boolean success = !serviceBindingOomAdjPolicy()
+                            || mService.mOomAdjuster.evaluateProviderConnectionAdd(r, cpr.proc)
+                            ? mService.updateOomAdjLocked(cpr.proc, OOM_ADJ_REASON_GET_PROVIDER)
+                            : true;
                     // XXX things have changed so updateOomAdjLocked doesn't actually tell us
                     // if the process has been successfully adjusted.  So to reduce races with
                     // it, we will check whether the process still exists.  Note that this doesn't
@@ -1529,7 +1532,9 @@
             }
             mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
                     cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
-            if (updateOomAdj) {
+            if (updateOomAdj && (!serviceBindingOomAdjPolicy()
+                    || mService.mOomAdjuster.evaluateProviderConnectionRemoval(conn.client,
+                            cpr.proc))) {
                 mService.updateOomAdjLocked(conn.provider.proc, OOM_ADJ_REASON_REMOVE_PROVIDER);
             }
         }
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index abc7ab1..db47e3f 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -46,9 +46,17 @@
     boolean hasBound;
     /** Set when the service's onUnbind() has asked to be told about new clients. */
     boolean doRebind;
-    
+
     String stringName;      // caching of toString
-    
+
+    /**
+     * Mark if we've skipped oom adj update before calling into the {@link Service#onBind()}
+     * or {@link Service#onUnbind()}.
+     *
+     * <p>If it's true, we'll skip the oom adj update too during the serviceDoneExecuting.
+     */
+    boolean mSkippedOomAdj;
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("service="); pw.println(service);
         dumpInService(pw, prefix);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ef7a0e0..862542e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -103,6 +103,7 @@
 import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
 import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
+import static com.android.server.am.ProcessList.FREEZER_CUTOFF_ADJ;
 import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
 import static com.android.server.am.ProcessList.HOME_APP_ADJ;
 import static com.android.server.am.ProcessList.INVALID_ADJ;
@@ -2309,7 +2310,7 @@
                     }
 
                     computeServiceHostOomAdjLSP(cr, app, cr.binding.client, now, topApp, doingAll,
-                            cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
+                            cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false);
 
                     adj = state.getCurRawAdj();
                     procState = state.getCurRawProcState();
@@ -2341,7 +2342,7 @@
                     ContentProviderConnection conn = cpr.connections.get(i);
                     ProcessRecord client = conn.client;
                     computeProviderHostOomAdjLSP(conn, app, client, now, topApp, doingAll,
-                            cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
+                            cycleReEval, computeClients, oomAdjReason, cachedAdj, true, false);
 
                     adj = state.getCurRawAdj();
                     procState = state.getCurRawProcState();
@@ -2558,17 +2559,18 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    protected void computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
+    protected boolean computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
             ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
             boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
-            boolean couldRecurse) {
+            boolean couldRecurse, boolean dryRun) {
         if (app.isPendingFinishAttach()) {
             // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
-            return;
+            return false;
         }
 
         final ProcessStateRecord state = app.mState;
         ProcessStateRecord cstate = client.mState;
+        boolean updated = false;
 
         if (couldRecurse) {
             if (app.isSdkSandbox && cr.binding.attributedClient != null) {
@@ -2599,19 +2601,25 @@
         final int prevRawAdj = adj;
         final int prevProcState = procState;
         final int prevSchedGroup = schedGroup;
+        final int prevCapability = capability;
 
         final int appUid = app.info.uid;
         final int logUid = mService.mCurOomAdjUid;
 
-        state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
-                || cstate.isCurBoundByNonBgRestrictedApp()
-                || clientProcState <= PROCESS_STATE_BOUND_TOP
-                || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
-                        && !cstate.isBackgroundRestricted()));
+        if (!dryRun) {
+            state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
+                    || cstate.isCurBoundByNonBgRestrictedApp()
+                    || clientProcState <= PROCESS_STATE_BOUND_TOP
+                    || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+                            && !cstate.isBackgroundRestricted()));
+        }
 
         if (client.mOptRecord.shouldNotFreeze()) {
             // Propagate the shouldNotFreeze flag down the bindings.
-            app.mOptRecord.setShouldNotFreeze(true);
+            if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+                // Bail out early, as we only care about the return value for a dryrun.
+                return true;
+            }
         }
 
         boolean trackedProcState = false;
@@ -2653,7 +2661,7 @@
             }
 
             if (couldRecurse && shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
-                return;
+                return false;
             }
 
             if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
@@ -2666,7 +2674,10 @@
             if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
                 // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
                 if (clientAdj < CACHED_APP_MIN_ADJ) {
-                    app.mOptRecord.setShouldNotFreeze(true);
+                    if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+                        // Bail out early, as we only care about the return value for a dryrun.
+                        return true;
+                    }
                 }
                 // Not doing bind OOM management, so treat
                 // this guy more like a started service.
@@ -2678,7 +2689,10 @@
                     if (adj > clientAdj) {
                         adjType = "cch-bound-ui-services";
                     }
-                    state.setCached(false);
+                    if (state.setCached(false, dryRun)) {
+                        // Bail out early, as we only care about the return value for a dryrun.
+                        return true;
+                    }
                     clientAdj = adj;
                     clientProcState = procState;
                 } else {
@@ -2721,7 +2735,9 @@
                             newAdj = PERSISTENT_SERVICE_ADJ;
                             schedGroup = SCHED_GROUP_DEFAULT;
                             procState = ActivityManager.PROCESS_STATE_PERSISTENT;
-                            cr.trackProcState(procState, mAdjSeq);
+                            if (!dryRun) {
+                                cr.trackProcState(procState, mAdjSeq);
+                            }
                             trackedProcState = true;
                         }
                     } else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE)
@@ -2762,11 +2778,16 @@
                         }
                     }
                     if (!cstate.isCached()) {
-                        state.setCached(false);
+                        if (state.setCached(false, dryRun)) {
+                            // Bail out early, as we only care about the return value for a dryrun.
+                            return true;
+                        }
                     }
                     if (adj >  newAdj) {
                         adj = newAdj;
-                        state.setCurRawAdj(adj);
+                        if (state.setCurRawAdj(adj, dryRun)) {
+                            // Bail out early, as we only care about the return value for a dryrun.
+                        }
                         adjType = "service";
                     }
                 }
@@ -2833,25 +2854,35 @@
 
             if (cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP) && clientIsSystem) {
                 schedGroup = SCHED_GROUP_TOP_APP;
-                state.setScheduleLikeTopApp(true);
+                if (dryRun) {
+                    if (prevSchedGroup < schedGroup) {
+                        // Bail out early, as we only care about the return value for a dryrun.
+                        return true;
+                    }
+                } else {
+                    state.setScheduleLikeTopApp(true);
+                }
             }
 
-            if (!trackedProcState) {
+            if (!trackedProcState && !dryRun) {
                 cr.trackProcState(clientProcState, mAdjSeq);
             }
 
             if (procState > clientProcState) {
                 procState = clientProcState;
-                state.setCurRawProcState(procState);
+                if (state.setCurRawProcState(procState, dryRun)) {
+                    // Bail out early, as we only care about the return value for a dryrun.
+                    return true;
+                }
                 if (adjType == null) {
                     adjType = "service";
                 }
             }
             if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
-                    && cr.hasFlag(Context.BIND_SHOWING_UI)) {
+                    && cr.hasFlag(Context.BIND_SHOWING_UI) && !dryRun) {
                 app.setPendingUiClean(true);
             }
-            if (adjType != null) {
+            if (adjType != null && !dryRun) {
                 state.setAdjType(adjType);
                 state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                         .REASON_SERVICE_IN_USE);
@@ -2876,11 +2907,16 @@
             // bound by an unfrozen app via a WPRI binding has to remain
             // unfrozen.
             if (clientAdj < CACHED_APP_MIN_ADJ) {
-                app.mOptRecord.setShouldNotFreeze(true);
+                if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+                    // Bail out early, as we only care about the return value for a dryrun.
+                    return true;
+                }
             }
         }
         if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
-            app.mServices.setTreatLikeActivity(true);
+            if (!dryRun) {
+                app.mServices.setTreatLikeActivity(true);
+            }
             if (clientProcState <= PROCESS_STATE_CACHED_ACTIVITY
                     && procState > PROCESS_STATE_CACHED_ACTIVITY) {
                 // This is a cached process, but somebody wants us to treat it like it has
@@ -2894,7 +2930,9 @@
             if (a != null && adj > FOREGROUND_APP_ADJ
                     && a.isActivityVisible()) {
                 adj = FOREGROUND_APP_ADJ;
-                state.setCurRawAdj(adj);
+                if (state.setCurRawAdj(adj, dryRun)) {
+                    return true;
+                }
                 if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) {
                     if (cr.hasFlag(Context.BIND_IMPORTANT)) {
                         schedGroup = SCHED_GROUP_TOP_APP_BOUND;
@@ -2902,16 +2940,18 @@
                         schedGroup = SCHED_GROUP_DEFAULT;
                     }
                 }
-                state.setCached(false);
-                state.setAdjType("service");
-                state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
-                        .REASON_SERVICE_IN_USE);
-                state.setAdjSource(a);
-                state.setAdjSourceProcState(procState);
-                state.setAdjTarget(cr.binding.service.instanceName);
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
-                            "Raise to service w/activity: " + app);
+                if (!dryRun) {
+                    state.setCached(false);
+                    state.setAdjType("service");
+                    state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
+                            .REASON_SERVICE_IN_USE);
+                    state.setAdjSource(a);
+                    state.setAdjSourceProcState(procState);
+                    state.setAdjTarget(cr.binding.service.instanceName);
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
+                                "Raise to service w/activity: " + app);
+                    }
                 }
             }
         }
@@ -2922,7 +2962,15 @@
         if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
             capability &= ~PROCESS_CAPABILITY_BFSL;
         }
+        if (!updated) {
+            updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
+                || (capability != prevCapability
+                        && (capability & prevCapability) == prevCapability);
+        }
 
+        if (dryRun) {
+            return updated;
+        }
         if (adj < prevRawAdj) {
             schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
         }
@@ -2935,15 +2983,16 @@
         state.setCurCapability(capability);
 
         state.setEmpty(false);
+        return updated;
     }
 
-    protected void computeProviderHostOomAdjLSP(ContentProviderConnection conn, ProcessRecord app,
-            ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
-            boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
-            boolean couldRecurse) {
+    protected boolean computeProviderHostOomAdjLSP(ContentProviderConnection conn,
+            ProcessRecord app, ProcessRecord client, long now, ProcessRecord topApp,
+            boolean doingAll, boolean cycleReEval, boolean computeClients, int oomAdjReason,
+            int cachedAdj, boolean couldRecurse, boolean dryRun) {
         if (app.isPendingFinishAttach()) {
             // We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
-            return;
+            return false;
         }
 
         final ProcessStateRecord state = app.mState;
@@ -2951,7 +3000,7 @@
 
         if (client == app) {
             // Being our own client is not interesting.
-            return;
+            return false;
         }
         if (couldRecurse) {
             if (computeClients) {
@@ -2964,7 +3013,7 @@
 
             if (shouldSkipDueToCycle(app, cstate, state.getCurRawProcState(), state.getCurRawAdj(),
                     cycleReEval)) {
-                return;
+                return false;
             }
         }
 
@@ -2979,6 +3028,7 @@
         final int prevRawAdj = adj;
         final int prevProcState = procState;
         final int prevSchedGroup = schedGroup;
+        final int prevCapability = capability;
 
         final int appUid = app.info.uid;
         final int logUid = mService.mCurOomAdjUid;
@@ -2995,14 +3045,19 @@
         }
         if (client.mOptRecord.shouldNotFreeze()) {
             // Propagate the shouldNotFreeze flag down the bindings.
-            app.mOptRecord.setShouldNotFreeze(true);
+            if (app.mOptRecord.setShouldNotFreeze(true, dryRun)) {
+                // Bail out early, as we only care about the return value for a dryrun.
+                return true;
+            }
         }
 
-        state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
-                || cstate.isCurBoundByNonBgRestrictedApp()
-                || clientProcState <= PROCESS_STATE_BOUND_TOP
-                || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
-                        && !cstate.isBackgroundRestricted()));
+        if (!dryRun) {
+            state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
+                    || cstate.isCurBoundByNonBgRestrictedApp()
+                    || clientProcState <= PROCESS_STATE_BOUND_TOP
+                    || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+                            && !cstate.isBackgroundRestricted()));
+        }
 
         String adjType = null;
         if (adj > clientAdj) {
@@ -3011,10 +3066,16 @@
                 adjType = "cch-ui-provider";
             } else {
                 adj = Math.max(clientAdj, FOREGROUND_APP_ADJ);
-                state.setCurRawAdj(adj);
+                if (state.setCurRawAdj(adj, dryRun)) {
+                    // Bail out early, as we only care about the return value for a dryrun.
+                    return true;
+                }
                 adjType = "provider";
             }
-            state.setCached(state.isCached() & cstate.isCached());
+            if (state.setCached(state.isCached() & cstate.isCached(), dryRun)) {
+                // Bail out early, as we only care about the return value for a dryrun.
+                return true;
+            }
         }
 
         if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
@@ -3028,15 +3089,20 @@
             }
         }
 
-        conn.trackProcState(clientProcState, mAdjSeq);
+        if (!dryRun) {
+            conn.trackProcState(clientProcState, mAdjSeq);
+        }
         if (procState > clientProcState) {
             procState = clientProcState;
-            state.setCurRawProcState(procState);
+            if (state.setCurRawProcState(procState, dryRun)) {
+                // Bail out early, as we only care about the return value for a dryrun.
+                return true;
+            }
         }
         if (cstate.getCurrentSchedulingGroup() > schedGroup) {
             schedGroup = SCHED_GROUP_DEFAULT;
         }
-        if (adjType != null) {
+        if (adjType != null && !dryRun) {
             state.setAdjType(adjType);
             state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                     .REASON_PROVIDER_IN_USE);
@@ -3056,6 +3122,12 @@
             capability &= ~PROCESS_CAPABILITY_BFSL;
         }
 
+        if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup
+                || (capability != prevCapability
+                        && (capability & prevCapability) == prevCapability))) {
+            return true;
+        }
+
         if (adj < prevRawAdj) {
             schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
         }
@@ -3068,6 +3140,7 @@
         state.setCurCapability(capability);
 
         state.setEmpty(false);
+        return false;
     }
 
     protected int getDefaultCapability(ProcessRecord app, int procState) {
@@ -3342,7 +3415,7 @@
             changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
         }
 
-        updateAppFreezeStateLSP(app, oomAdjReson);
+        updateAppFreezeStateLSP(app, oomAdjReson, false);
 
         if (state.getReportedProcState() != state.getCurProcState()) {
             state.setReportedProcState(state.getCurProcState());
@@ -3727,7 +3800,8 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
+    void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason,
+            boolean immediate) {
         if (!mCachedAppOptimizer.useFreezer()) {
             return;
         }
@@ -3746,10 +3820,14 @@
 
         final ProcessStateRecord state = app.mState;
         // Use current adjustment when freezing, set adjustment when unfreezing.
-        if (state.getCurAdj() >= CACHED_APP_MIN_ADJ && !opt.isFrozen()
+        if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen()
                 && !opt.shouldNotFreeze()) {
-            mCachedAppOptimizer.freezeAppAsyncLSP(app);
-        } else if (state.getSetAdj() < CACHED_APP_MIN_ADJ) {
+            if (!immediate) {
+                mCachedAppOptimizer.freezeAppAsyncLSP(app);
+            } else {
+                mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app);
+            }
+        } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) {
             mCachedAppOptimizer.unfreezeAppLSP(app,
                     CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
         }
@@ -3826,4 +3904,89 @@
         // The caller will set the initial value in this implementation.
         return app.mState.isCurBoundByNonBgRestrictedApp();
     }
+
+    /**
+     * Evaluate the service connection, return {@code true} if the client will change the state
+     * of the service host process by the given connection.
+     */
+    @GuardedBy("mService")
+    boolean evaluateServiceConnectionAdd(ProcessRecord client, ProcessRecord app,
+            ConnectionRecord cr) {
+        if (evaluateConnectionPrelude(client, app)) {
+            return true;
+        }
+        if (app.getSetAdj() <= client.getSetAdj()
+                && app.getSetProcState() <= client.getSetProcState()
+                && ((app.getSetCapability() & client.getSetCapability())
+                        == client.getSetCapability()
+                        || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES
+                                | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS))) {
+            // The service host process has better states than the client, so no change.
+            return false;
+        }
+        // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive
+        // since it's only evaluating one service connection.
+        return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(),
+                mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
+                CACHED_APP_MIN_ADJ, false, true /* dryRun */);
+    }
+
+    @GuardedBy("mService")
+    boolean evaluateServiceConnectionRemoval(ProcessRecord client, ProcessRecord app,
+            ConnectionRecord cr) {
+        if (evaluateConnectionPrelude(client, app)) {
+            return true;
+        }
+
+        if (app.getSetAdj() < client.getSetAdj()
+                && app.getSetProcState() < client.getSetProcState()) {
+            // The service host process has better states than the client.
+            if (((app.getSetCapability() & client.getSetCapability()) == PROCESS_CAPABILITY_NONE)
+                    || cr.notHasFlag(Context.BIND_INCLUDE_CAPABILITIES
+                            | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) {
+                // The service host app doesn't get any capabilities from the client.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @GuardedBy("mService")
+    boolean evaluateProviderConnectionAdd(ProcessRecord client, ProcessRecord app) {
+        if (evaluateConnectionPrelude(client, app)) {
+            return true;
+        }
+        if (app.getSetAdj() <= client.getSetAdj()
+                && app.getSetProcState() <= client.getSetProcState()) {
+            // The provider host process has better states than the client, so no change.
+            return false;
+        }
+        return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(),
+                mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ,
+                false, true /* dryRun */);
+    }
+
+    @GuardedBy("mService")
+    boolean evaluateProviderConnectionRemoval(ProcessRecord client, ProcessRecord app) {
+        if (evaluateConnectionPrelude(client, app)) {
+            return true;
+        }
+        if (app.getSetAdj() < client.getSetAdj()
+                && app.getSetProcState() < client.getSetProcState()) {
+            // The provider host process has better states than the client, so no change.
+            return false;
+        }
+        return true;
+    }
+
+    private boolean evaluateConnectionPrelude(ProcessRecord client, ProcessRecord app) {
+        if (client == null || app == null) {
+            return true;
+        }
+        if (app.isSdkSandbox || app.isolated || app.isKilledByAm() || app.isKilled()) {
+            // Let's always re-evaluate them for now.
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 5a3fbe9..f85b03e 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -1002,7 +1002,7 @@
 
 
             computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false);
+                    oomAdjReason, cachedAdj, false, false);
         }
 
         for (int i = psr.numberOfSdkSandboxConnections() - 1; i >= 0; i--) {
@@ -1018,7 +1018,7 @@
             }
 
             computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false);
+                    oomAdjReason, cachedAdj, false, false);
         }
 
         final ProcessProviderRecord ppr = app.mProviders;
@@ -1035,7 +1035,7 @@
             }
 
             computeProviderHostOomAdjLSP(cpc, provider, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false);
+                    oomAdjReason, cachedAdj, false, false);
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index f5c5ea8..a8fe734 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -274,7 +274,20 @@
 
     @GuardedBy("mProcLock")
     void setShouldNotFreeze(boolean shouldNotFreeze) {
+        setShouldNotFreeze(shouldNotFreeze, false);
+    }
+
+    /**
+     * @return {@code true} if it's a dry run and it's going to unfreeze the process
+     * if it was a real run.
+     */
+    @GuardedBy("mProcLock")
+    boolean setShouldNotFreeze(boolean shouldNotFreeze, boolean dryRun) {
+        if (dryRun) {
+            return mFrozen && !shouldNotFreeze;
+        }
         mShouldNotFreeze = shouldNotFreeze;
+        return false;
     }
 
     @GuardedBy("mProcLock")
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f5c34a5..10cd6e5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -371,6 +371,12 @@
     private static final long LMKD_RECONNECT_DELAY_MS = 1000;
 
     /**
+     * The cuttoff adj for the freezer, app processes with adj greater than this value will be
+     * eligible for the freezer.
+     */
+    static final int FREEZER_CUTOFF_ADJ = CACHED_APP_MIN_ADJ;
+
+    /**
      * Apps have no access to the private data directories of any other app, even if the other
      * app has made them world-readable.
      */
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e5c4a66..de6f034 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -720,6 +720,11 @@
         return mState.getSetProcState();
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    int getSetCapability() {
+        return mState.getSetCapability();
+    }
+
     @GuardedBy({"mService", "mProcLock"})
     public void makeActive(IApplicationThread thread, ProcessStatsService tracker) {
         mProfile.onProcessActive(thread, tracker);
@@ -1401,8 +1406,12 @@
 
     void onProcessUnfrozen() {
         mProfile.onProcessUnfrozen();
+        mServices.onProcessUnfrozen();
     }
 
+    void onProcessFrozenCancelled() {
+        mServices.onProcessFrozenCancelled();
+    }
 
     /*
      *  Delete all packages from list except the package indicated in info
@@ -1644,6 +1653,13 @@
         return mWasForceStopped;
     }
 
+    boolean isFreezable() {
+        return mService.mOomAdjuster.mCachedAppOptimizer.useFreezer()
+                && !mOptRecord.isFreezeExempt()
+                && !mOptRecord.shouldNotFreeze()
+                && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ;
+    }
+
     /**
      * Traverses all client processes and feed them to consumer.
      */
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index f5f2b10..57d233e 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -19,6 +19,8 @@
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_FOREGROUND_SERVICE;
 
+import static com.android.server.am.Flags.serviceBindingOomAdjPolicy;
+
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -144,6 +146,11 @@
      */
     private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
 
+    /**
+     * The process should schedule a service timeout timer but haven't done so.
+     */
+    private boolean mScheduleServiceTimeoutPending;
+
     final ProcessRecord mApp;
 
     private final ActivityManagerService mService;
@@ -657,6 +664,41 @@
         setHasClientActivities(false);
     }
 
+    @GuardedBy("mService")
+    void noteScheduleServiceTimeoutPending(boolean pending) {
+        mScheduleServiceTimeoutPending = pending;
+    }
+
+    @GuardedBy("mService")
+    boolean isScheduleServiceTimeoutPending() {
+        return mScheduleServiceTimeoutPending;
+    }
+
+    @GuardedBy("mService")
+    void onProcessUnfrozen() {
+        scheduleServiceTimeoutIfNeededLocked();
+    }
+
+    @GuardedBy("mService")
+    void onProcessFrozenCancelled() {
+        scheduleServiceTimeoutIfNeededLocked();
+    }
+
+    @GuardedBy("mService")
+    private void scheduleServiceTimeoutIfNeededLocked() {
+        if (!serviceBindingOomAdjPolicy()) {
+            return;
+        }
+        if (mScheduleServiceTimeoutPending && mExecutingServices.size() > 0) {
+            mService.mServices.scheduleServiceTimeoutLocked(mApp);
+            // We'll need to reset the executingStart since the app was frozen.
+            final long now = SystemClock.uptimeMillis();
+            for (int i = 0, size = mExecutingServices.size(); i < size; i++) {
+                mExecutingServices.valueAt(i).executingStart = now;
+            }
+        }
+    }
+
     void dump(PrintWriter pw, String prefix, long nowUptime) {
         if (mHasForegroundServices || mApp.mState.getForcingToImportant() != null) {
             pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
@@ -701,5 +743,10 @@
                 pw.print(prefix); pw.print("  - "); pw.println(mConnections.valueAt(i));
             }
         }
+        if (serviceBindingOomAdjPolicy()) {
+            pw.print(prefix);
+            pw.print("scheduleServiceTimeoutPending=");
+            pw.println(mScheduleServiceTimeoutPending);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 3391ec7..8362eaf 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -479,9 +479,22 @@
 
     @GuardedBy({"mService", "mProcLock"})
     void setCurRawAdj(int curRawAdj) {
+        setCurRawAdj(curRawAdj, false);
+    }
+
+    /**
+     * @return {@code true} if it's a dry run and it's going to bump the adj score of the process
+     * if it was a real run.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    boolean setCurRawAdj(int curRawAdj, boolean dryRun) {
+        if (dryRun) {
+            return mCurRawAdj > curRawAdj;
+        }
         mCurRawAdj = curRawAdj;
         mApp.getWindowProcessController().setPerceptible(
                 curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
+        return false;
     }
 
     @GuardedBy(anyOf = {"mService", "mProcLock"})
@@ -594,7 +607,20 @@
 
     @GuardedBy({"mService", "mProcLock"})
     void setCurRawProcState(int curRawProcState) {
+        setCurRawProcState(curRawProcState, false);
+    }
+
+    /**
+     * @return {@code true} if it's a dry run and it's going to bump the procstate of the process
+     * if it was a real run.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    boolean setCurRawProcState(int curRawProcState, boolean dryRun) {
+        if (dryRun) {
+            return mCurRawProcState > curRawProcState;
+        }
         mCurRawProcState = curRawProcState;
+        return false;
     }
 
     @GuardedBy(anyOf = {"mService", "mProcLock"})
@@ -900,7 +926,20 @@
 
     @GuardedBy("mService")
     void setCached(boolean cached) {
+        setCached(cached, false);
+    }
+
+    /**
+     * @return {@code true} if it's a dry run and it's going to uncache the process
+     * if it was a real run.
+     */
+    @GuardedBy("mService")
+    boolean setCached(boolean cached, boolean dryRun) {
+        if (dryRun) {
+            return mCached && !cached;
+        }
         mCached = cached;
+        return false;
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 654aebd..31d9cc9 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -35,3 +35,10 @@
      description: "Enable the new FGS restriction logic"
      bug: "276963716"
 }
+
+flag {
+    name: "service_binding_oom_adj_policy"
+    namespace: "backstage_power"
+    description: "Optimize the service bindings by different policies like skipping oom adjuster"
+    bug: "318717054"
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9cf9119..245fcea 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2776,17 +2776,17 @@
     }
 
     private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
+        final ScreenCapture.DisplayCaptureArgs captureArgs;
         synchronized (mSyncRoot) {
             final IBinder token = getDisplayToken(displayId);
             if (token == null) {
                 return null;
             }
 
-            final ScreenCapture.DisplayCaptureArgs captureArgs =
-                    new ScreenCapture.DisplayCaptureArgs.Builder(token)
+            captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token)
                             .build();
-            return ScreenCapture.captureDisplay(captureArgs);
         }
+        return ScreenCapture.captureDisplay(captureArgs);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5def428..4767ebd 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2389,16 +2389,6 @@
             @StartInputReason int startInputReason,
             int unverifiedTargetSdkVersion,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        String selectedMethodId = getSelectedMethodIdLocked();
-
-        if (!mSystemReady) {
-            // If the system is not yet ready, we shouldn't be running third
-            // party code.
-            return new InputBindResult(
-                    InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
-                    null, null, null, selectedMethodId, getSequenceNumberLocked(), false);
-        }
-
         if (!InputMethodUtils.checkIfPackageBelongsToUid(mPackageManagerInternal, cs.mUid,
                 editorInfo.packageName)) {
             Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
@@ -2419,6 +2409,7 @@
 
         // Potentially override the selected input method if the new display belongs to a virtual
         // device with a custom IME.
+        String selectedMethodId = getSelectedMethodIdLocked();
         if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
             final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
             if (deviceMethodId == null) {
@@ -3663,7 +3654,6 @@
                         + "specified for cross-user startInputOrWindowGainedFocus()");
             }
         }
-
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -3675,6 +3665,14 @@
                     "InputMethodManagerService#startInputOrWindowGainedFocus");
             final InputBindResult result;
             synchronized (ImfLock.class) {
+                if (!mSystemReady) {
+                    // If the system is not yet ready, we shouldn't be running third arty code.
+                    return new InputBindResult(
+                            InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
+                            null /* method */, null /* accessibilitySessions */, null /* channel */,
+                            getSelectedMethodIdLocked(), getSequenceNumberLocked(),
+                            false /* isInputMethodSuppressingSpellChecker */);
+                }
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 5e38bca..2522f7b 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -27,6 +27,7 @@
 
 import android.annotation.Nullable;
 import android.location.Location;
+import android.location.LocationRequest;
 import android.location.LocationResult;
 import android.location.provider.ProviderRequest;
 import android.os.SystemClock;
@@ -179,6 +180,7 @@
     private void onThrottlingChangedLocked(boolean deliverImmediate) {
         long throttlingIntervalMs = INTERVAL_DISABLED;
         if (mDeviceStationary && mDeviceIdle && !mIncomingRequest.isLocationSettingsIgnored()
+                && mIncomingRequest.getQuality() != LocationRequest.QUALITY_HIGH_ACCURACY
                 && mLastLocation != null
                 && mLastLocation.getElapsedRealtimeAgeMillis(mDeviceStationaryRealtimeMs)
                 <= MAX_STATIONARY_LOCATION_AGE_MS) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a97652c..7e3254d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3437,7 +3437,7 @@
                 }
 
                 str.close();
-            } catch (IOException | XmlPullParserException e) {
+            } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) {
                 // Remove corrupted file and retry.
                 atomicFile.failRead(str, e);
 
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 14db70e..23d0230 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -288,6 +288,28 @@
      * configuration.
      */
     private static UserTypeDetails.Builder getDefaultTypeProfilePrivate() {
+        UserProperties.Builder userPropertiesBuilder = new UserProperties.Builder()
+                .setStartWithParent(true)
+                .setCredentialShareableWithParent(true)
+                .setAuthAlwaysRequiredToDisableQuietMode(true)
+                .setAllowStoppingUserWithDelayedLocking(true)
+                .setMediaSharedWithParent(false)
+                .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
+                .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+                .setShowInQuietMode(
+                        UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
+                .setShowInSharingSurfaces(
+                        UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
+                .setCrossProfileIntentFilterAccessControl(
+                        UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
+                .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
+                .setCrossProfileContentSharingStrategy(
+                        UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT);
+        if (android.multiuser.Flags.supportHidingProfiles()) {
+            userPropertiesBuilder.setProfileApiVisibility(
+                    UserProperties.PROFILE_API_VISIBILITY_HIDDEN);
+        }
+
         return new UserTypeDetails.Builder()
                 .setName(USER_TYPE_PROFILE_PRIVATE)
                 .setBaseType(FLAG_PROFILE)
@@ -306,23 +328,7 @@
                 .setDarkThemeBadgeColors(
                         R.color.white)
                 .setDefaultRestrictions(getDefaultProfileRestrictions())
-                .setDefaultUserProperties(new UserProperties.Builder()
-                        .setStartWithParent(true)
-                        .setCredentialShareableWithParent(true)
-                        .setAuthAlwaysRequiredToDisableQuietMode(true)
-                        .setAllowStoppingUserWithDelayedLocking(true)
-                        .setMediaSharedWithParent(false)
-                        .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
-                        .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
-                        .setShowInQuietMode(
-                                UserProperties.SHOW_IN_QUIET_MODE_HIDDEN)
-                        .setShowInSharingSurfaces(
-                                UserProperties.SHOW_IN_SHARING_SURFACES_SEPARATE)
-                        .setCrossProfileIntentFilterAccessControl(
-                                UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
-                        .setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT)
-                        .setCrossProfileContentSharingStrategy(
-                                UserProperties.CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT));
+                .setDefaultUserProperties(userPropertiesBuilder);
     }
 
     /**
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 d0fe964..6ed2d31 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -421,6 +421,11 @@
         if (ai.isArchived) {
             ai.nonLocalizedLabel = state.getArchiveState().getActivityInfos().get(0).getTitle();
         }
+        if (!state.isInstalled() && !state.dataExists()
+                && android.content.pm.Flags.nullableDataDir()) {
+            // The data dir has been deleted
+            ai.dataDir = null;
+        }
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d9fa01e..03d55d9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -53,6 +53,7 @@
 import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
 import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
 import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static android.content.Context.CONTEXT_RESTRICTED;
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_HOME;
 import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -2231,7 +2232,17 @@
 
         mOptInOnBackInvoked = WindowOnBackInvokedDispatcher
                 .isOnBackInvokedCallbackEnabled(info, info.applicationInfo,
-                        () -> ent != null ? ent.array : null, false);
+                        () -> {
+                            Context appContext = null;
+                            try {
+                                appContext = mAtmService.mContext.createPackageContextAsUser(
+                                        info.packageName, CONTEXT_RESTRICTED,
+                                        UserHandle.of(mUserId));
+                                appContext.setTheme(theme);
+                            } catch (PackageManager.NameNotFoundException ignore) {
+                            }
+                            return appContext;
+                        });
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4681396..5423c66 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -997,7 +997,7 @@
                         BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
                                 state.mAppSwitchState);
                         if (balAllowedForUid.allows()) {
-                            return balAllowedForCaller.withProcessInfo("process", proc);
+                            return balAllowedForUid.withProcessInfo("process", proc);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e7ecf52..5dcd335 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -160,6 +160,7 @@
 import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields;
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
+import static com.android.window.flags.Flags.deferDisplayUpdates;
 import static com.android.window.flags.Flags.explicitRefreshRateHints;
 
 import android.annotation.IntDef;
@@ -174,7 +175,6 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -246,7 +246,6 @@
 import android.window.DisplayWindowPolicyController;
 import android.window.IDisplayAreaOrganizer;
 import android.window.ScreenCapture;
-import android.window.ScreenCapture.SynchronousScreenCaptureListener;
 import android.window.SystemPerformanceHinter;
 import android.window.TransitionRequestInfo;
 
@@ -276,7 +275,6 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
-import static com.android.window.flags.Flags.deferDisplayUpdates;
 
 /**
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
@@ -5207,10 +5205,9 @@
     }
 
     /**
-     * Takes a snapshot of the display.  In landscape mode this grabs the whole screen.
-     * In portrait mode, it grabs the full screenshot.
+     * Creates a LayerCaptureArgs object to represent the entire DisplayContent
      */
-    Bitmap screenshotDisplayLocked() {
+    ScreenCapture.LayerCaptureArgs getLayerCaptureArgs() {
         if (!mWmService.mPolicy.isScreenOn()) {
             if (DEBUG_SCREENSHOT) {
                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -5218,24 +5215,10 @@
             return null;
         }
 
-        SynchronousScreenCaptureListener syncScreenCapture =
-                ScreenCapture.createSyncCaptureListener();
-
         getBounds(mTmpRect);
         mTmpRect.offsetTo(0, 0);
-        ScreenCapture.LayerCaptureArgs args =
-                new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl())
-                        .setSourceCrop(mTmpRect).build();
-
-        ScreenCapture.captureLayers(args, syncScreenCapture);
-
-        final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
-                syncScreenCapture.getBuffer();
-        final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
-        if (bitmap == null) {
-            Slog.w(TAG_WM, "Failed to take screenshot");
-        }
-        return bitmap;
+        return new ScreenCapture.LayerCaptureArgs.Builder(getSurfaceControl())
+                .setSourceCrop(mTmpRect).build();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 10405ec..e027eb6 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1135,16 +1135,17 @@
                     if (!mFreezeTaskListReordering) {
                         // Simple case: this is not an affiliated task, so we just move it to the
                         // front unless overridden by the provided activity options
+                        int indexToAdd = findIndexToAdd(task);
                         mTasks.remove(taskIndex);
-                        mTasks.add(0, task);
+                        mTasks.add(indexToAdd, task);
                         if (taskIndex != 0) {
                             // Only notify when position changes
                             mTaskNotificationController.notifyTaskListUpdated();
                         }
 
                         if (DEBUG_RECENTS) {
-                            Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
-                                    + " from " + taskIndex);
+                            Slog.d(TAG_RECENTS, "addRecent: moving " + task + " to index "
+                                    + indexToAdd + " from " + taskIndex);
                         }
                     }
                     notifyTaskPersisterLocked(task, false);
@@ -1231,6 +1232,37 @@
         notifyTaskPersisterLocked(task, false /* flush */);
     }
 
+    // Looks for a new index to move the recent Task. Note that the recent Task should not be
+    // placed higher than another recent Task that has higher hierarchical z-ordering.
+    private int findIndexToAdd(Task task) {
+        int indexToAdd = 0;
+        for (int i = 0; i < mTasks.size(); i++) {
+            final Task otherTask = mTasks.get(i);
+            if (task == otherTask) {
+                break;
+            }
+
+            if (!otherTask.isAttached()) {
+                // Stop searching if not attached.
+                break;
+            }
+
+            if (otherTask.inPinnedWindowingMode()) {
+                // Skip pip task without increasing index since pip is always on screen.
+                continue;
+            }
+
+            // Stop searching if the task has higher z-ordering, or increase the index and
+            // continue the search.
+            if (task.compareTo(otherTask) > 0) {
+                break;
+            }
+
+            indexToAdd = i + 1;
+        }
+        return indexToAdd;
+    }
+
     /**
      * Add the task to the bottom if possible.
      */
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 399815b..6949a87 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -279,13 +279,14 @@
             return null;
         }
         Point largestDisplaySize = new Point();
+        float largestWidth = 0;
         List<DisplayInfo> possibleDisplayInfo =
                 mService.getPossibleDisplayInfoLocked(DEFAULT_DISPLAY);
         for (int i = 0; i < possibleDisplayInfo.size(); i++) {
             DisplayInfo displayInfo = possibleDisplayInfo.get(i);
-            if (displayInfo.type == Display.TYPE_INTERNAL
-                    && Math.max(displayInfo.logicalWidth, displayInfo.logicalHeight)
-                    > Math.max(largestDisplaySize.x, largestDisplaySize.y)) {
+            float width = (float) displayInfo.logicalWidth / displayInfo.physicalXDpi;
+            if (displayInfo.type == Display.TYPE_INTERNAL && width > largestWidth) {
+                largestWidth = width;
                 largestDisplaySize.set(displayInfo.logicalWidth,
                         displayInfo.logicalHeight);
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9650b8bc..426694d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4087,7 +4087,7 @@
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
 
-        final Bitmap bm;
+        ScreenCapture.LayerCaptureArgs captureArgs;
         synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(DEFAULT_DISPLAY);
             if (displayContent == null) {
@@ -4095,12 +4095,30 @@
                     Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId="
                             + DEFAULT_DISPLAY);
                 }
-                bm = null;
+                captureArgs = null;
             } else {
-                bm = displayContent.screenshotDisplayLocked();
+                captureArgs = displayContent.getLayerCaptureArgs();
             }
         }
 
+        final Bitmap bm;
+        if (captureArgs != null) {
+            ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
+                    ScreenCapture.createSyncCaptureListener();
+
+            ScreenCapture.captureLayers(captureArgs, syncScreenCapture);
+
+            final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+                    syncScreenCapture.getBuffer();
+            bm = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+        } else {
+            bm = null;
+        }
+
+        if (bm == null) {
+            Slog.w(TAG_WM, "Failed to take screenshot");
+        }
+
         FgThread.getHandler().post(() -> {
             try {
                 receiver.onHandleAssistScreenshot(bm);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4ba52e4..3f889c0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1471,7 +1471,12 @@
                         final int index = task.mChildren.indexOf(topTaskFragment);
                         task.mChildren.remove(taskFragment);
                         task.mChildren.add(index, taskFragment);
-                        effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                        if (taskFragment.hasChild()) {
+                            effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                        } else {
+                            // Ensure that the child layers are updated if the TaskFragment is empty
+                            task.assignChildLayers();
+                        }
                     }
                 }
                 break;
@@ -1486,7 +1491,12 @@
                 if (task != null) {
                     task.mChildren.remove(taskFragment);
                     task.mChildren.add(0, taskFragment);
-                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                    if (taskFragment.hasChild()) {
+                        effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                    } else {
+                        // Ensure that the child layers are updated if the TaskFragment is empty.
+                        task.assignChildLayers();
+                    }
                 }
                 break;
             }
@@ -1495,7 +1505,12 @@
                 if (task != null) {
                     task.mChildren.remove(taskFragment);
                     task.mChildren.add(taskFragment);
-                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                    if (taskFragment.hasChild()) {
+                        effects |= TRANSACT_EFFECTS_LIFECYCLE;
+                    } else {
+                        // Ensure that the child layers are updated if the TaskFragment is empty.
+                        task.assignChildLayers();
+                    }
                 }
                 break;
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f288103..519c9bb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -71,6 +71,7 @@
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
 import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER;
@@ -484,6 +485,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.net.module.util.ProxyUtils;
+import com.android.net.thread.flags.Flags;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
@@ -13339,6 +13341,11 @@
                 UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS});
         USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS});
+        if (Flags.threadUserRestrictionEnabled()) {
+            USER_RESTRICTION_PERMISSIONS.put(
+                    UserManager.DISALLOW_THREAD_NETWORK,
+                    new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});
+        }
         USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
         USER_RESTRICTION_PERMISSIONS.put(
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index 3aca1ca..f8accc3 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -103,6 +103,7 @@
         ":PackageParserTestApp4",
         ":PackageParserTestApp5",
         ":PackageParserTestApp6",
+        ":PackageParserTestApp7",
     ],
     resource_zips: [":PackageManagerServiceServerTests_apks_as_resources"],
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
index 71f5c75..a0e0e1e 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java
@@ -15,6 +15,17 @@
  */
 package com.android.server.pm;
 
+import static android.content.UriRelativeFilter.PATH;
+import static android.content.UriRelativeFilter.QUERY;
+import static android.content.UriRelativeFilter.FRAGMENT;
+import static android.content.UriRelativeFilterGroup.ACTION_ALLOW;
+import static android.content.UriRelativeFilterGroup.ACTION_BLOCK;
+import static android.os.PatternMatcher.PATTERN_ADVANCED_GLOB;
+import static android.os.PatternMatcher.PATTERN_LITERAL;
+import static android.os.PatternMatcher.PATTERN_PREFIX;
+import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
+import static android.os.PatternMatcher.PATTERN_SUFFIX;
+
 import static com.android.internal.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -36,11 +47,15 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.IntentFilter;
+import android.content.UriRelativeFilter;
+import android.content.UriRelativeFilterGroup;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.Flags;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.Property;
 import android.content.pm.ServiceInfo;
@@ -50,6 +65,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.ArraySet;
 
 import androidx.annotation.Nullable;
@@ -106,6 +124,7 @@
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -123,6 +142,9 @@
     @Rule
     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private File mTmpDir;
     private static final File FRAMEWORK = new File("/system/framework/framework-res.apk");
     private static final String TEST_APP1_APK = "PackageParserTestApp1.apk";
@@ -131,6 +153,7 @@
     private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
     private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
     private static final String TEST_APP6_APK = "PackageParserTestApp6.apk";
+    private static final String TEST_APP7_APK = "PackageParserTestApp7.apk";
     private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
 
     @Before
@@ -375,6 +398,87 @@
         assertNotEquals("$automotive", actualDisplayCategory);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_RELATIVE_REFERENCE_INTENT_FILTERS)
+    public void testParseUriRelativeFilterGroups() throws Exception {
+        final File testFile = extractFile(TEST_APP7_APK);
+        try {
+            final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+            final List<ParsedActivity> activities = pkg.getActivities();
+            final List<ParsedIntentInfo> intents = activities.get(0).getIntents();
+            final IntentFilter intentFilter = intents.get(0).getIntentFilter();
+            assertEquals(7, intentFilter.countUriRelativeFilterGroups());
+
+            UriRelativeFilterGroup group = intentFilter.getUriRelativeFilterGroup(0);
+            Collection<UriRelativeFilter> filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_BLOCK, group.getAction());
+            assertEquals(3, filters.size());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_PREFIX, "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SIMPLE_GLOB,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_LITERAL,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(1);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertEquals(2, filters.size());
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_LITERAL,
+                    "query=string")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SUFFIX,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(2);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_LITERAL, "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_LITERAL,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_LITERAL,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(3);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_PREFIX, "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_PREFIX,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_PREFIX,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(4);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_SIMPLE_GLOB,
+                    "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SIMPLE_GLOB,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SIMPLE_GLOB,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(5);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_ADVANCED_GLOB,
+                    "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_ADVANCED_GLOB,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_ADVANCED_GLOB,
+                    "fragment")));
+
+            group = intentFilter.getUriRelativeFilterGroup(6);
+            filters = group.getUriRelativeFilters();
+            assertEquals(ACTION_ALLOW, group.getAction());
+            assertTrue(filters.contains(new UriRelativeFilter(PATH, PATTERN_SUFFIX, "/gizmos")));
+            assertTrue(filters.contains(new UriRelativeFilter(QUERY, PATTERN_SUFFIX,
+                    ".*query=string.*")));
+            assertTrue(filters.contains(new UriRelativeFilter(FRAGMENT, PATTERN_SUFFIX,
+                    "fragment")));
+        } finally {
+            testFile.delete();
+        }
+    }
+
     private static final int PROPERTY_TYPE_BOOLEAN = 1;
     private static final int PROPERTY_TYPE_FLOAT = 2;
     private static final int PROPERTY_TYPE_INTEGER = 3;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
new file mode 100644
index 0000000..2f12a3b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -0,0 +1,677 @@
+/*
+ * 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.am;
+
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+import static android.content.Context.BIND_WAIVE_PRIORITY;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.server.am.ProcessList.HOME_APP_ADJ;
+import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
+import static com.android.server.am.ProcessList.SERVICE_ADJ;
+import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService.Injector;
+import com.android.server.am.ApplicationExitInfoTest.ServiceThreadRule;
+import com.android.server.appop.AppOpsService;
+import com.android.server.firewall.IntentFirewall;
+import com.android.server.wm.ActivityTaskManagerService;
+import com.android.server.wm.WindowProcessController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.function.Consumer;
+
+/**
+ * Test class for the service timeout.
+ *
+ * Build/Install/Run:
+ *  atest ServiceBindingOomAdjPolicyTest
+ */
+@Presubmit
+public final class ServiceBindingOomAdjPolicyTest {
+    private static final String TAG = ServiceBindingOomAdjPolicyTest.class.getSimpleName();
+
+    private static final String TEST_APP1_NAME = "com.example.foo";
+    private static final String TEST_SERVICE1_NAME = "com.example.foo.Foobar";
+    private static final int TEST_APP1_UID = 10123;
+    private static final int TEST_APP1_PID = 12345;
+
+    private static final String TEST_APP2_NAME = "com.example.bar";
+    private static final String TEST_SERVICE2_NAME = "com.example.bar.Buz";
+    private static final int TEST_APP2_UID = 10124;
+    private static final int TEST_APP2_PID = 12346;
+
+    @Rule
+    public final ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
+    private Context mContext;
+    private HandlerThread mHandlerThread;
+
+    @Mock
+    private AppOpsService mAppOpsService;
+    @Mock
+    private DropBoxManagerInternal mDropBoxManagerInt;
+    @Mock
+    private PackageManagerInternal mPackageManagerInt;
+    @Mock
+    private UsageStatsManagerInternal mUsageStatsManagerInt;
+    @Mock
+    private AppErrors mAppErrors;
+    @Mock
+    private IntentFirewall mIntentFirewall;
+
+    private ActivityManagerService mAms;
+    private ProcessList mProcessList;
+    private ActiveServices mActiveServices;
+
+    private int mCurrentCallingUid;
+    private int mCurrentCallingPid;
+
+    /** Run at the test class initialization */
+    @BeforeClass
+    public static void setUpOnce() {
+        System.setProperty("dexmaker.share_classloader", "true");
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        final ProcessList realProcessList = new ProcessList();
+        mProcessList = spy(realProcessList);
+
+        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+        LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+
+        final ActivityManagerService realAms = new ActivityManagerService(
+                new TestInjector(mContext), mServiceThreadRule.getThread());
+        final ActivityTaskManagerService realAtm = new ActivityTaskManagerService(mContext);
+        realAtm.initialize(null, null, mContext.getMainLooper());
+        realAms.mActivityTaskManager = spy(realAtm);
+        realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+        realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
+        realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
+        realAms.mPackageManagerInt = mPackageManagerInt;
+        realAms.mUsageStatsService = mUsageStatsManagerInt;
+        realAms.mAppProfiler = spy(realAms.mAppProfiler);
+        realAms.mProcessesReady = true;
+        mAms = spy(realAms);
+        realProcessList.mService = mAms;
+
+        doReturn(false).when(mPackageManagerInt).filterAppAccess(anyString(), anyInt(), anyInt());
+        doReturn(true).when(mIntentFirewall).checkService(any(), any(), anyInt(), anyInt(), any(),
+                any());
+        doReturn(false).when(mAms.mAtmInternal).hasSystemAlertWindowPermission(anyInt(), anyInt(),
+                any());
+        doReturn(true).when(mAms.mOomAdjuster.mCachedAppOptimizer).useFreezer();
+        doNothing().when(mAms.mOomAdjuster.mCachedAppOptimizer).freezeAppAsyncInternalLSP(
+                any(), anyLong(), anyBoolean());
+        doReturn(false).when(mAms.mAppProfiler).updateLowMemStateLSP(anyInt(), anyInt(),
+                anyInt(), anyLong());
+
+        mCurrentCallingUid = TEST_APP1_UID;
+        mCurrentCallingPid = TEST_APP1_PID;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        mHandlerThread.quit();
+    }
+
+    @Test
+    public void testServiceSelfBindingOomAdj() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be 0 oom adj updates.
+        performTestServiceSelfBindingOomAdj(never(), never());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update.
+        performTestServiceSelfBindingOomAdj(atLeastOnce(), atLeastOnce());
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void performTestServiceSelfBindingOomAdj(VerificationMode bindMode,
+            VerificationMode unbindMode) throws Exception {
+        final ProcessRecord app = addProcessRecord(
+                TEST_APP1_PID,           // pid
+                TEST_APP1_UID,           // uid
+                PROCESS_STATE_SERVICE,   // procstate
+                SERVICE_ADJ,             // adj
+                PROCESS_CAPABILITY_NONE, // capabilities
+                TEST_APP1_NAME           // packageName
+        );
+        final Intent serviceIntent = createServiceIntent(TEST_APP1_NAME, TEST_SERVICE1_NAME,
+                TEST_APP1_UID);
+        final IServiceConnection serviceConnection = mock(IServiceConnection.class);
+
+        // Make a self binding.
+        assertNotEquals(0, mAms.bindService(
+                app.getThread(),         // caller
+                null,                    // token
+                serviceIntent,           // service
+                null,                    // resolveType
+                serviceConnection,       // connection
+                BIND_AUTO_CREATE,        // flags
+                TEST_APP1_NAME,          // callingPackage
+                USER_SYSTEM              // userId
+        ));
+
+        verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt());
+        clearInvocations(mAms.mOomAdjuster);
+
+        // Unbind the service.
+        mAms.unbindService(serviceConnection);
+
+        verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt());
+        clearInvocations(mAms.mOomAdjuster);
+
+        removeProcessRecord(app);
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjMoreImportant() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjLessImportant() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be 0 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                never(), never());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjWaivePriority() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be 0 oom adj update for binding
+        // because we're using the BIND_WAIVE_PRIORITY;
+        // but for the unbinding, because client is better than service, we can't skip it safely.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                never(), atLeastOnce());
+
+        // Verify that there should be 0 oom adj update
+        // because we're using the BIND_WAIVE_PRIORITY;
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE | BIND_WAIVE_PRIORITY,
+                never(), never());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because the client is more important.
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                PROCESS_CAPABILITY_NONE, TEST_APP1_NAME,
+                this::setHasForegroundServices,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_HOME,
+                HOME_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHomeProcess,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjNoIncludeCapabilities() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be 0 oom adj update
+        // because we didn't specify the "BIND_INCLUDE_CAPABILITIES"
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ,
+                PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                never(), never());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ,
+                PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjWithIncludeCapabilities() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        // because we use the "BIND_INCLUDE_CAPABILITIES"
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ,
+                PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE | BIND_INCLUDE_CAPABILITIES,
+                atLeastOnce(), atLeastOnce());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_HOME, HOME_APP_ADJ,
+                PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, TEST_APP1_NAME,
+                this::setHomeProcess,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE | BIND_INCLUDE_CAPABILITIES,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @Test
+    public void testServiceDistinctBindingOomAdjFreezeCaller() throws Exception {
+        // Enable the flags.
+        mSetFlagsRule.enableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be 0 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE,
+                TEST_APP1_NAME, null,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                never(), never());
+
+        // Disable the flags.
+        mSetFlagsRule.disableFlags(Flags.FLAG_SERVICE_BINDING_OOM_ADJ_POLICY);
+
+        // Verify that there should be at least 1 oom adj update
+        performTestServiceDistinctBindingOomAdj(TEST_APP1_PID, TEST_APP1_UID,
+                PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ, PROCESS_CAPABILITY_NONE,
+                TEST_APP1_NAME, null,
+                TEST_APP2_PID, TEST_APP2_UID, PROCESS_STATE_FOREGROUND_SERVICE,
+                PERCEPTIBLE_APP_ADJ, PROCESS_CAPABILITY_NONE, TEST_APP2_NAME, TEST_SERVICE2_NAME,
+                this::setHasForegroundServices,
+                BIND_AUTO_CREATE,
+                atLeastOnce(), atLeastOnce());
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void performTestServiceDistinctBindingOomAdj(int clientPid, int clientUid,
+            int clientProcState, int clientAdj, int clientCap, String clientPackageName,
+            Consumer<ProcessRecord> clientAppFixer,
+            int servicePid, int serviceUid, int serviceProcState, int serviceAdj,
+            int serviceCap, String servicePackageName, String serviceName,
+            Consumer<ProcessRecord> serviceAppFixer, int bindingFlags,
+            VerificationMode bindMode, VerificationMode unbindMode) throws Exception {
+        final ProcessRecord clientApp = addProcessRecord(
+                clientPid,
+                clientUid,
+                clientProcState,
+                clientAdj,
+                clientCap,
+                clientPackageName
+        );
+        final ProcessRecord serviceApp = addProcessRecord(
+                servicePid,
+                serviceUid,
+                serviceProcState,
+                serviceAdj,
+                serviceCap,
+                servicePackageName
+        );
+        final Intent serviceIntent = createServiceIntent(servicePackageName, serviceName,
+                serviceUid);
+        final IServiceConnection serviceConnection = mock(IServiceConnection.class);
+        if (clientAppFixer != null) clientAppFixer.accept(clientApp);
+        if (serviceAppFixer != null) serviceAppFixer.accept(serviceApp);
+
+        // Make a self binding.
+        assertNotEquals(0, mAms.bindService(
+                clientApp.getThread(), // caller
+                null,                  // token
+                serviceIntent,         // service
+                null,                  // resolveType
+                serviceConnection,     // connection
+                bindingFlags,          // flags
+                clientPackageName,     // callingPackage
+                USER_SYSTEM            // userId
+        ));
+
+        verify(mAms.mOomAdjuster, bindMode).updateOomAdjPendingTargetsLocked(anyInt());
+        clearInvocations(mAms.mOomAdjuster);
+
+        if (clientApp.isFreezable()) {
+            verify(mAms.mOomAdjuster.mCachedAppOptimizer,
+                    times(Flags.serviceBindingOomAdjPolicy() ? 1 : 0))
+                    .freezeAppAsyncInternalLSP(eq(clientApp), eq(0L), anyBoolean());
+            clearInvocations(mAms.mOomAdjuster.mCachedAppOptimizer);
+        }
+
+        // Unbind the service.
+        mAms.unbindService(serviceConnection);
+
+        verify(mAms.mOomAdjuster, unbindMode).updateOomAdjPendingTargetsLocked(anyInt());
+        clearInvocations(mAms.mOomAdjuster);
+
+        removeProcessRecord(clientApp);
+        removeProcessRecord(serviceApp);
+    }
+
+    private void setHasForegroundServices(ProcessRecord app) {
+        app.mServices.setHasForegroundServices(true,
+                FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, false);
+    }
+
+    private void setHomeProcess(ProcessRecord app) {
+        final WindowProcessController wpc = app.getWindowProcessController();
+        doReturn(true).when(wpc).isHomeProcess();
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private ProcessRecord addProcessRecord(int pid, int uid, int procState, int adj, int cap,
+                String packageName) {
+        final IApplicationThread appThread = mock(IApplicationThread.class);
+        final IBinder threadBinder = mock(IBinder.class);
+        final ProcessRecord app = makeProcessRecord(pid, uid, uid, null, 0,
+                procState, adj, cap, 0L, 0L, packageName, packageName, mAms);
+
+        app.makeActive(appThread, mAms.mProcessStats);
+        doReturn(threadBinder).when(appThread).asBinder();
+        mProcessList.addProcessNameLocked(app);
+        mProcessList.updateLruProcessLocked(app, false, null);
+
+        setFieldValue(ProcessRecord.class, app, "mWindowProcessController",
+                mock(WindowProcessController.class));
+
+        doReturn(app.getSetCapability()).when(mAms.mOomAdjuster).getDefaultCapability(
+                eq(app), anyInt());
+
+        return app;
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private Intent createServiceIntent(String packageName, String serviceName, int serviceUid) {
+        final ComponentName compName = new ComponentName(packageName, serviceName);
+        final Intent serviceIntent = new Intent().setComponent(compName);
+        final ResolveInfo rInfo = new ResolveInfo();
+        rInfo.serviceInfo = makeServiceInfo(compName.getClassName(), compName.getPackageName(),
+                serviceUid);
+        doReturn(rInfo).when(mPackageManagerInt).resolveService(any(Intent.class), any(),
+                anyLong(), anyInt(), anyInt());
+
+        return serviceIntent;
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void removeProcessRecord(ProcessRecord app) {
+        app.setKilled(true);
+        mProcessList.removeProcessNameLocked(app.processName, app.uid);
+        mProcessList.removeLruProcessLocked(app);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
+            int connectionGroup, int procState, int adj, int cap, long pss, long rss,
+            String processName, String packageName, ActivityManagerService ams) {
+        final ProcessRecord app = ApplicationExitInfoTest.makeProcessRecord(pid, uid, packageUid,
+                definingUid, connectionGroup, procState, pss, rss, processName, packageName, ams);
+        app.mState.setCurProcState(procState);
+        app.mState.setSetProcState(procState);
+        app.mState.setCurAdj(adj);
+        app.mState.setSetAdj(adj);
+        app.mState.setCurCapability(cap);
+        app.mState.setSetCapability(cap);
+        app.mState.setCached(procState >= PROCESS_STATE_LAST_ACTIVITY || adj >= CACHED_APP_MIN_ADJ);
+        return app;
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private ServiceInfo makeServiceInfo(String serviceName, String packageName, int packageUid) {
+        final ServiceInfo sInfo = new ServiceInfo();
+        sInfo.name = serviceName;
+        sInfo.processName = packageName;
+        sInfo.packageName = packageName;
+        sInfo.applicationInfo = new ApplicationInfo();
+        sInfo.applicationInfo.uid = packageUid;
+        sInfo.applicationInfo.packageName = packageName;
+        sInfo.exported = true;
+        return sInfo;
+    }
+
+    private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+        try {
+            Field field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            Field mfield = Field.class.getDeclaredField("accessFlags");
+            mfield.setAccessible(true);
+            mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+            field.set(obj, val);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+        }
+    }
+
+    private class TestInjector extends Injector {
+        TestInjector(Context context) {
+            super(context);
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+                Handler handler) {
+            return mAppOpsService;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public ProcessList getProcessList(ActivityManagerService service) {
+            return mProcessList;
+        }
+
+        @Override
+        public ActiveServices getActiveServices(ActivityManagerService service) {
+            if (mActiveServices == null) {
+                mActiveServices = spy(new ActiveServices(service));
+            }
+            return mActiveServices;
+        }
+
+        @Override
+        public int getCallingUid() {
+            return mCurrentCallingUid;
+        }
+
+        @Override
+        public int getCallingPid() {
+            return mCurrentCallingPid;
+        }
+
+        @Override
+        public long clearCallingIdentity() {
+            return (((long) mCurrentCallingUid) << 32) | mCurrentCallingPid;
+        }
+
+        @Override
+        public void restoreCallingIdentity(long ident) {
+        }
+
+        @Override
+        public AppErrors getAppErrors() {
+            return mAppErrors;
+        }
+
+        @Override
+        public IntentFirewall getIntentFirewall() {
+            return mIntentFirewall;
+        }
+    }
+
+    // TODO: [b/302724778] Remove manual JNI load
+    static {
+        System.loadLibrary("mockingservicestestjni");
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 293391f..c6608e6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -45,6 +45,7 @@
 import static com.android.server.job.controllers.JobStatus.NO_EARLIEST_RUNTIME;
 import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -78,6 +79,7 @@
 
 import java.time.Clock;
 import java.time.ZoneOffset;
+import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
 public class JobStatusTest {
@@ -138,6 +140,35 @@
     }
 
     @Test
+    public void testApplyBasicPiiFilters_email() {
+        assertEquals("[EMAIL]", JobStatus.applyBasicPiiFilters("test@email.com"));
+        assertEquals("[EMAIL]", JobStatus.applyBasicPiiFilters("test+plus@email.com"));
+        assertEquals("[EMAIL]", JobStatus.applyBasicPiiFilters("t.e_st+plus-minus@email.com"));
+
+        assertEquals("prefix:[EMAIL]", JobStatus.applyBasicPiiFilters("prefix:test@email.com"));
+
+        assertEquals("not-an-email", JobStatus.applyBasicPiiFilters("not-an-email"));
+    }
+
+    @Test
+    public void testApplyBasicPiiFilters_mixture() {
+        assertEquals("[PHONE]:[EMAIL]",
+                JobStatus.applyBasicPiiFilters("123-456-7890:test+plus@email.com"));
+        assertEquals("prefix:[PHONE]:[EMAIL]",
+                JobStatus.applyBasicPiiFilters("prefix:123-456-7890:test+plus@email.com"));
+    }
+
+    @Test
+    public void testApplyBasicPiiFilters_phone() {
+        assertEquals("[PHONE]", JobStatus.applyBasicPiiFilters("123-456-7890"));
+        assertEquals("[PHONE]", JobStatus.applyBasicPiiFilters("+1-234-567-8900"));
+
+        assertEquals("prefix:[PHONE]", JobStatus.applyBasicPiiFilters("prefix:123-456-7890"));
+
+        assertEquals("not-a-phone-number", JobStatus.applyBasicPiiFilters("not-a-phone-number"));
+    }
+
+    @Test
     public void testCanRunInBatterySaver_regular() {
         final JobInfo jobInfo =
                 new JobInfo.Builder(101, new ComponentName("foo", "bar")).build();
@@ -245,6 +276,42 @@
     }
 
     @Test
+    public void testGetFilteredDebugTags() {
+        final JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                .addDebugTag("test@email.com")
+                .addDebugTag("123-456-7890")
+                .addDebugTag("random")
+                .build();
+        JobStatus job = createJobStatus(jobInfo);
+        String[] expected = new String[]{"[EMAIL]", "[PHONE]", "random"};
+        String[] result = job.getFilteredDebugTags();
+        Arrays.sort(expected);
+        Arrays.sort(result);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void testGetFilteredTraceTag() {
+        JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                .setTraceTag("test@email.com")
+                .build();
+        JobStatus job = createJobStatus(jobInfo);
+        assertEquals("[EMAIL]", job.getFilteredTraceTag());
+
+        jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                .setTraceTag("123-456-7890")
+                .build();
+        job = createJobStatus(jobInfo);
+        assertEquals("[PHONE]", job.getFilteredTraceTag());
+
+        jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
+                .setTraceTag("random")
+                .build();
+        job = createJobStatus(jobInfo);
+        assertEquals("random", job.getFilteredTraceTag());
+    }
+
+    @Test
     public void testIsUserVisibleJob() {
         JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
                 .setUserInitiated(false)
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
index 8d9a6c5..9a143d5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/altitude/AltitudeConverterTest.java
@@ -21,6 +21,8 @@
 import static org.junit.Assert.assertThrows;
 
 import android.content.Context;
+import android.frameworks.location.altitude.GetGeoidHeightRequest;
+import android.frameworks.location.altitude.GetGeoidHeightResponse;
 import android.location.Location;
 import android.location.altitude.AltitudeConverter;
 
@@ -176,4 +178,20 @@
         assertThrows(IllegalArgumentException.class,
                 () -> mAltitudeConverter.addMslAltitudeToLocation(mContext, location));
     }
+
+    @Test
+    public void testGetGeoidHeight_expectedBehavior() throws IOException {
+        GetGeoidHeightRequest request = new GetGeoidHeightRequest();
+        request.latitudeDegrees = -35.334815;
+        request.longitudeDegrees = -45;
+        // Requires data to be loaded from raw assets.
+        GetGeoidHeightResponse response = mAltitudeConverter.getGeoidHeight(mContext, request);
+        assertThat(response.geoidHeightMeters).isWithin(2).of(-5.0622);
+        assertThat(response.geoidHeightErrorMeters).isGreaterThan(0f);
+        assertThat(response.geoidHeightErrorMeters).isLessThan(1f);
+        assertThat(response.expirationDistanceMeters).isWithin(1).of(-6.33);
+        assertThat(response.additionalGeoidHeightErrorMeters).isGreaterThan(0f);
+        assertThat(response.additionalGeoidHeightErrorMeters).isLessThan(1f);
+        assertThat(response.success).isTrue();
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 4eba219..efab19c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -29,6 +29,7 @@
 
 import android.content.Context;
 import android.location.Location;
+import android.location.LocationRequest;
 import android.location.LocationResult;
 import android.location.provider.ProviderRequest;
 import android.platform.test.annotations.Presubmit;
@@ -218,4 +219,22 @@
         verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
         verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
     }
+
+    @Test
+    public void testNoThrottle_highAccuracy() {
+        ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(
+                50).setQuality(LocationRequest.QUALITY_HIGH_ACCURACY).build();
+
+        mProvider.getController().setRequest(request);
+        verify(mDelegate).onSetRequest(request);
+
+        LocationResult loc = createLocationResult("test_provider", mRandom);
+        mDelegateProvider.reportLocation(loc);
+        verify(mListener, times(1)).onReportLocation(loc);
+
+        mInjector.getDeviceStationaryHelper().setStationary(true);
+        mInjector.getDeviceIdleHelper().setIdle(true);
+        verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+        verify(mListener, after(75).times(1)).onReportLocation(any(LocationResult.class));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index d7ed7c2..8d8dc9c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -23,6 +23,7 @@
 import android.content.pm.UserProperties;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Xml;
 
 import androidx.test.filters.MediumTest;
@@ -31,6 +32,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -52,10 +54,13 @@
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class UserManagerServiceUserPropertiesTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     /** Test that UserProperties can properly read the xml information that it writes. */
     @Test
     public void testWriteReadXml() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         final UserProperties defaultProps = new UserProperties.Builder()
                 .setShowInLauncher(21)
                 .setStartWithParent(false)
@@ -73,6 +78,7 @@
                 .setDeleteAppWithParent(false)
                 .setAlwaysVisible(false)
                 .setCrossProfileContentSharingStrategy(0)
+                .setProfileApiVisibility(34)
                 .build();
         final UserProperties actualProps = new UserProperties(defaultProps);
         actualProps.setShowInLauncher(14);
@@ -90,6 +96,7 @@
         actualProps.setDeleteAppWithParent(true);
         actualProps.setAlwaysVisible(true);
         actualProps.setCrossProfileContentSharingStrategy(1);
+        actualProps.setProfileApiVisibility(36);
 
         // Write the properties to xml.
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -114,6 +121,7 @@
     /** Tests parcelling an object in which all properties are present. */
     @Test
     public void testParcelUnparcel() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         final UserProperties originalProps = new UserProperties.Builder()
                 .setShowInLauncher(2145)
                 .build();
@@ -124,6 +132,7 @@
     /** Tests copying a UserProperties object varying permissions. */
     @Test
     public void testCopyLacksPermissions() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         final UserProperties defaultProps = new UserProperties.Builder()
                 .setShowInLauncher(2145)
                 .setStartWithParent(true)
@@ -134,6 +143,7 @@
                 .setAuthAlwaysRequiredToDisableQuietMode(false)
                 .setAllowStoppingUserWithDelayedLocking(false)
                 .setAlwaysVisible(true)
+                .setProfileApiVisibility(110)
                 .build();
         final UserProperties orig = new UserProperties(defaultProps);
         orig.setShowInLauncher(2841);
@@ -209,6 +219,8 @@
                 copy::isCredentialShareableWithParent, true);
         assertEqualGetterOrThrows(orig::getCrossProfileContentSharingStrategy,
                 copy::getCrossProfileContentSharingStrategy, true);
+        assertEqualGetterOrThrows(orig::getProfileApiVisibility, copy::getProfileApiVisibility,
+                true);
     }
 
     /**
@@ -270,5 +282,6 @@
         assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible());
         assertThat(expected.getCrossProfileContentSharingStrategy())
                 .isEqualTo(actual.getCrossProfileContentSharingStrategy());
+        assertThat(expected.getProfileApiVisibility()).isEqualTo(actual.getProfileApiVisibility());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 7083706..1ee604e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -41,6 +41,7 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 
 import androidx.test.InstrumentationRegistry;
@@ -50,6 +51,7 @@
 import com.android.frameworks.servicestests.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -71,9 +73,11 @@
     public void setup() {
         mResources = InstrumentationRegistry.getTargetContext().getResources();
     }
-
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Test
     public void testUserTypeBuilder_createUserType() {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
         final Bundle systemSettings = makeSettingsBundle("s1", "s2");
         final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
@@ -97,7 +101,8 @@
                 .setInheritDevicePolicy(340)
                 .setDeleteAppWithParent(true)
                 .setAlwaysVisible(true)
-                .setCrossProfileContentSharingStrategy(1);
+                .setCrossProfileContentSharingStrategy(1)
+                .setProfileApiVisibility(34);
 
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
@@ -180,6 +185,7 @@
         assertTrue(type.getDefaultUserPropertiesReference().getAlwaysVisible());
         assertEquals(1, type.getDefaultUserPropertiesReference()
                 .getCrossProfileContentSharingStrategy());
+        assertEquals(34, type.getDefaultUserPropertiesReference().getProfileApiVisibility());
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -199,6 +205,7 @@
 
     @Test
     public void testUserTypeBuilder_defaults() {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("name") // Required (no default allowed)
                 .setBaseType(FLAG_FULL) // Required (no default allowed)
@@ -238,6 +245,8 @@
                 props.getShowInQuietMode());
         assertEquals(UserProperties.CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
                 props.getCrossProfileContentSharingStrategy());
+        assertEquals(UserProperties.PROFILE_API_VISIBILITY_VISIBLE,
+                props.getProfileApiVisibility());
 
         assertFalse(type.hasBadge());
     }
@@ -310,6 +319,7 @@
     /** Tests {@link UserTypeFactory#customizeBuilders} for a reasonable xml file. */
     @Test
     public void testUserTypeFactoryCustomize_profile() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         final String userTypeAosp1 = "android.test.1"; // Profile user that is not customized
         final String userTypeAosp2 = "android.test.2"; // Profile user that is customized
         final String userTypeOem1 = "custom.test.1"; // Custom-defined profile
@@ -332,7 +342,8 @@
                 .setShowInQuietMode(24)
                 .setDeleteAppWithParent(true)
                 .setAlwaysVisible(false)
-                .setCrossProfileContentSharingStrategy(1);
+                .setCrossProfileContentSharingStrategy(1)
+                .setProfileApiVisibility(36);
 
         final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
         builders.put(userTypeAosp1, new UserTypeDetails.Builder()
@@ -383,6 +394,7 @@
         assertFalse(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
         assertEquals(1, aospType.getDefaultUserPropertiesReference()
                 .getCrossProfileContentSharingStrategy());
+        assertEquals(36, aospType.getDefaultUserPropertiesReference().getProfileApiVisibility());
 
         // userTypeAosp2 should be modified.
         aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -439,6 +451,7 @@
         assertTrue(aospType.getDefaultUserPropertiesReference().getAlwaysVisible());
         assertEquals(0, aospType.getDefaultUserPropertiesReference()
                 .getCrossProfileContentSharingStrategy());
+        assertEquals(36, aospType.getDefaultUserPropertiesReference().getProfileApiVisibility());
 
         // userTypeOem1 should be created.
         UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 06be456..db561c4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -39,6 +39,7 @@
 import android.os.UserManager;
 import android.platform.test.annotations.Postsubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -55,6 +56,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -97,6 +99,8 @@
     private UserSwitchWaiter mUserSwitchWaiter;
     private UserRemovalWaiter mUserRemovalWaiter;
     private int mOriginalCurrentUserId;
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() throws Exception {
@@ -168,6 +172,7 @@
 
     @Test
     public void testCloneUser() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         assumeCloneEnabled();
         UserHandle mainUser = mUserManager.getMainUser();
         assumeTrue("Main user is null", mainUser != null);
@@ -224,6 +229,7 @@
                 .isEqualTo(cloneUserProperties.getCrossProfileContentSharingStrategy());
         assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
         assertThrows(SecurityException.class, cloneUserProperties::getAlwaysVisible);
+        assertThrows(SecurityException.class, cloneUserProperties::getProfileApiVisibility);
         compareDrawables(mUserManager.getUserBadge(),
                 Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
 
@@ -305,6 +311,7 @@
 
     @Test
     public void testPrivateProfile() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SUPPORT_HIDING_PROFILES);
         UserHandle mainUser = mUserManager.getMainUser();
         assumeTrue("Main user is null", mainUser != null);
         // Get the default properties for private profile user type.
@@ -346,7 +353,8 @@
         assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent);
         assertThrows(SecurityException.class,
                 privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking);
-
+        assertThrows(SecurityException.class,
+                privateProfileUserProperties::getProfileApiVisibility);
         compareDrawables(mUserManager.getUserBadge(),
                 Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
 
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index 3e78f9a..131b380 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -102,3 +102,17 @@
     resource_dirs: ["res"],
     manifest: "AndroidManifestApp6.xml",
 }
+
+android_test_helper_app {
+    name: "PackageParserTestApp7",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    resource_dirs: ["res"],
+    manifest: "AndroidManifestApp7.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml
new file mode 100644
index 0000000..cb87a48
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp7.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.packageparserapp" >
+
+    <application>
+        <activity android:name=".TestActivity"
+                  android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="http"
+                    android:host="www.example.com" />
+                <uri-relative-filter-group android:allow="false">
+                    <data android:pathPrefix="/gizmos" />
+                    <data android:queryPattern=".*query=string.*" />
+                    <data android:fragment="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:query="query=string" />
+                    <data android:fragmentSuffix="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:path="/gizmos" />
+                    <data android:query=".*query=string.*" />
+                    <data android:fragment="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:pathPrefix="/gizmos" />
+                    <data android:queryPrefix=".*query=string.*" />
+                    <data android:fragmentPrefix="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:pathPattern="/gizmos" />
+                    <data android:queryPattern=".*query=string.*" />
+                    <data android:fragmentPattern="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:pathAdvancedPattern="/gizmos" />
+                    <data android:queryAdvancedPattern=".*query=string.*" />
+                    <data android:fragmentAdvancedPattern="fragment" />
+                </uri-relative-filter-group>
+                <uri-relative-filter-group>
+                    <data android:pathSuffix="/gizmos" />
+                    <data android:querySuffix=".*query=string.*" />
+                    <data android:fragmentSuffix="fragment" />
+                </uri-relative-filter-group>
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
index d255271..e9ece5d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTransactionTests.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
@@ -23,6 +26,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.times;
 
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
@@ -35,6 +39,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Test class for {@link WindowContainerTransaction}.
  *
@@ -45,7 +52,6 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class WindowContainerTransactionTests extends WindowTestsBase {
-
     @Test
     public void testRemoveTask() {
         final Task rootTask = createTask(mDisplayContent);
@@ -72,6 +78,123 @@
         verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask);
     }
 
+    @Test
+    public void testDesktopMode_tasksAreBroughtToFront() {
+        final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
+        TaskDisplayArea tda = desktopOrganizer.mDefaultTDA;
+        List<ActivityRecord> activityRecords = new ArrayList<>();
+        int numberOfTasks = 4;
+        desktopOrganizer.createFreeformTasksWithActivities(desktopOrganizer,
+                activityRecords, numberOfTasks);
+
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        // Bring home to front of the tasks
+        desktopOrganizer.bringHomeToFront();
+
+        // Bring tasks in front of the home
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        desktopOrganizer.bringDesktopTasksToFront(wct);
+        applyTransaction(wct);
+
+        // Verify tasks are resumed and in correct z-order
+        verify(mRootWindowContainer, times(2)).ensureActivitiesVisible();
+        for (int i = 0; i < numberOfTasks - 1; i++) {
+            assertTrue(tda.mChildren
+                    .indexOf(desktopOrganizer.mTasks.get(i).getRootTask())
+                    < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(i + 1).getRootTask()));
+        }
+    }
+
+    @Test
+    public void testDesktopMode_moveTaskToDesktop() {
+        final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
+        TaskDisplayArea tda = desktopOrganizer.mDefaultTDA;
+        List<ActivityRecord> activityRecords = new ArrayList<>();
+        int numberOfTasks = 4;
+        desktopOrganizer.createFreeformTasksWithActivities(desktopOrganizer,
+                activityRecords, numberOfTasks);
+
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        // Bring home to front of the tasks
+        desktopOrganizer.bringHomeToFront();
+
+        // Bring tasks in front of the home and newly moved task to on top of them
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        desktopOrganizer.bringDesktopTasksToFront(wct);
+        desktopOrganizer.addMoveToDesktopChanges(wct, task, true);
+        wct.setBounds(task.getTaskInfo().token, desktopOrganizer.getDefaultDesktopTaskBounds());
+        applyTransaction(wct);
+
+        // Verify tasks are resumed
+        verify(mRootWindowContainer, times(2)).ensureActivitiesVisible();
+
+        // Tasks are in correct z-order
+        for (int i = 0; i < numberOfTasks - 1; i++) {
+            assertTrue(tda.mChildren
+                    .indexOf(desktopOrganizer.mTasks.get(i).getRootTask())
+                    < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(i + 1).getRootTask()));
+        }
+        // New task is on top of other tasks
+        assertTrue(tda.mChildren
+                .indexOf(desktopOrganizer.mTasks.get(3).getRootTask())
+                < tda.mChildren.indexOf(task));
+
+        // New task is in freeform and has specified bounds
+        assertEquals(WINDOWING_MODE_FREEFORM, task.getWindowingMode());
+        assertEquals(desktopOrganizer.getDefaultDesktopTaskBounds(), task.getBounds());
+    }
+
+
+    @Test
+    public void testDesktopMode_moveTaskToFullscreen() {
+        final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
+        List<ActivityRecord> activityRecords = new ArrayList<>();
+        int numberOfTasks = 4;
+        desktopOrganizer.createFreeformTasksWithActivities(desktopOrganizer,
+                activityRecords, numberOfTasks);
+
+        Task taskToMove = desktopOrganizer.mTasks.get(numberOfTasks - 1);
+
+        // Bring tasks in front of the home and newly moved task to on top of them
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        desktopOrganizer.addMoveToFullscreen(wct, taskToMove, false);
+        applyTransaction(wct);
+
+        // New task is in freeform
+        assertEquals(WINDOWING_MODE_FULLSCREEN, taskToMove.getWindowingMode());
+    }
+
+    @Test
+    public void testDesktopMode_moveTaskToFront() {
+        final TestDesktopOrganizer desktopOrganizer = new TestDesktopOrganizer(mAtm);
+        TaskDisplayArea tda = desktopOrganizer.mDefaultTDA;
+        List<ActivityRecord> activityRecords = new ArrayList<>();
+        int numberOfTasks = 5;
+        desktopOrganizer.createFreeformTasksWithActivities(desktopOrganizer,
+                activityRecords, numberOfTasks);
+
+        // Bring task 2 on top of other tasks
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reorder(desktopOrganizer.mTasks.get(2).getTaskInfo().token, true /* onTop */);
+        applyTransaction(wct);
+
+        // Tasks are in correct z-order
+        assertTrue(tda.mChildren.indexOf(desktopOrganizer.mTasks.get(0).getRootTask())
+                < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(1).getRootTask()));
+        assertTrue(tda.mChildren.indexOf(desktopOrganizer.mTasks.get(1).getRootTask())
+                < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(3).getRootTask()));
+        assertTrue(tda.mChildren.indexOf(desktopOrganizer.mTasks.get(3).getRootTask())
+                < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(4).getRootTask()));
+        assertTrue(tda.mChildren.indexOf(desktopOrganizer.mTasks.get(4).getRootTask())
+                < tda.mChildren.indexOf(desktopOrganizer.mTasks.get(2).getRootTask()));
+    }
+
     private Task createTask(int taskId) {
         return new Task.Builder(mAtm)
                 .setTaskId(taskId)
@@ -87,3 +210,4 @@
         }
     }
 }
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a0bafb6..be83744 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -116,6 +116,7 @@
 import android.window.TaskFragmentOrganizer;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
 
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.util.ArrayUtils;
@@ -1899,12 +1900,14 @@
         final int mDesktopModeDefaultWidthDp = 840;
         final int mDesktopModeDefaultHeightDp = 630;
         final int mDesktopDensity = 284;
+        final int mOverrideDensity = 285;
 
         final ActivityTaskManagerService mService;
         final TaskDisplayArea mDefaultTDA;
         List<Task> mTasks;
         final DisplayContent mDisplay;
         Rect mStableBounds;
+        Task mHomeTask;
 
         TestDesktopOrganizer(ActivityTaskManagerService service, DisplayContent display) {
             mService = service;
@@ -1913,8 +1916,8 @@
             mService.mTaskOrganizerController.registerTaskOrganizer(this);
             mTasks = new ArrayList<>();
             mStableBounds = display.getBounds();
+            mHomeTask =  mDefaultTDA.getRootHomeTask();
         }
-
         TestDesktopOrganizer(ActivityTaskManagerService service) {
             this(service, service.mTaskSupervisor.mRootWindowContainer.getDefaultDisplay());
         }
@@ -1929,8 +1932,10 @@
         }
 
         public Rect getDefaultDesktopTaskBounds() {
-            int width = (int) (mDesktopModeDefaultWidthDp * mDesktopDensity + 0.5f);
-            int height = (int) (mDesktopModeDefaultHeightDp * mDesktopDensity + 0.5f);
+            int width = (int) (mDesktopModeDefaultWidthDp
+                    * (mOverrideDensity / mDesktopDensity) + 0.5f);
+            int height = (int) (mDesktopModeDefaultHeightDp
+                    * (mOverrideDensity / mDesktopDensity) + 0.5f);
             Rect outBounds = new Rect();
 
             outBounds.set(0, 0, width, height);
@@ -1942,8 +1947,69 @@
             return outBounds;
         }
 
+        public void createFreeformTasksWithActivities(TestDesktopOrganizer desktopOrganizer,
+                List<ActivityRecord> activityRecords, int numberOfTasks) {
+            for (int i = 0; i < numberOfTasks; i++) {
+                Rect bounds = new Rect(desktopOrganizer.getDefaultDesktopTaskBounds());
+                bounds.offset(20 * i, 20 * i);
+                desktopOrganizer.createTask(bounds);
+            }
+
+            for (int i = 0; i < numberOfTasks; i++) {
+                activityRecords.add(new TaskBuilder(mService.mTaskSupervisor)
+                        .setParentTask(desktopOrganizer.mTasks.get(i))
+                        .setCreateActivity(true)
+                        .build()
+                        .getTopMostActivity());
+            }
+
+            for (int i = 0; i < numberOfTasks; i++) {
+                activityRecords.get(i).setVisibleRequested(true);
+            }
+
+            for (int i = 0; i < numberOfTasks; i++) {
+                assertEquals(desktopOrganizer.mTasks.get(i), activityRecords.get(i).getRootTask());
+            }
+        }
+
+        public void bringHomeToFront() {
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.reorder(mHomeTask.getTaskInfo().token, true /* onTop */);
+            applyTransaction(wct);
+        }
+
+        public void bringDesktopTasksToFront(WindowContainerTransaction wct) {
+            for (Task task: mTasks) {
+                wct.reorder(task.getTaskInfo().token, true /* onTop */);
+            }
+        }
+
+        public void addMoveToDesktopChanges(WindowContainerTransaction wct, Task task,
+                boolean overrideDensity) {
+            wct.setWindowingMode(task.getTaskInfo().token, WINDOWING_MODE_FREEFORM);
+            wct.reorder(task.getTaskInfo().token, true /* onTop */);
+            if (overrideDensity) {
+                wct.setDensityDpi(task.getTaskInfo().token, mOverrideDensity);
+            }
+        }
+
+        public void addMoveToFullscreen(WindowContainerTransaction wct, Task task,
+                boolean overrideDensity) {
+            wct.setWindowingMode(task.getTaskInfo().token, WINDOWING_MODE_FULLSCREEN);
+            wct.setBounds(task.getTaskInfo().token, new Rect());
+            if (overrideDensity) {
+                wct.setDensityDpi(task.getTaskInfo().token, mOverrideDensity);
+            }
+        }
+
+        private void applyTransaction(@androidx.annotation.NonNull WindowContainerTransaction wct) {
+            if (!wct.isEmpty()) {
+                mService.mWindowOrganizerController.applyTransaction(wct);
+            }
+        }
     }
 
+
     static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
         return createTestWindowToken(type, dc, false /* persistOnEmpty */);
     }
diff --git a/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
index b7712bd..655da740 100644
--- a/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
+++ b/telephony/java/android/telephony/satellite/stub/INtnSignalStrengthConsumer.aidl
@@ -16,7 +16,7 @@
 
 package android.telephony.satellite.stub;
 
-import android.telephony.satellite.NtnSignalStrength;
+import android.telephony.satellite.stub.NtnSignalStrength;
 
 /**
  * Consumer pattern for a request that receives the signal strength of non-terrestrial network from
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
index 5aaf30a..14230fe 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
@@ -134,7 +134,7 @@
                 c.drawText("Remote", 250, 250, paint);
                 surface.unlockCanvasAndPost(c);
                 WindowManager wm = getSystemService(WindowManager.class);
-                mInputToken = wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken,
+                wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken,
                         mSurfaceControl,
                         Choreographer.getInstance(), event -> {
                             Log.d(TAG, "onInputEvent-remote " + event);
@@ -147,11 +147,9 @@
         @Override
         public void tearDownEmbeddedSurfaceControl() {
             if (mSurfaceControl != null) {
-                new SurfaceControl.Transaction().remove(mSurfaceControl);
-            }
-            if (mInputToken != null) {
                 WindowManager wm = getSystemService(WindowManager.class);
-                wm.unregisterSurfaceControlInputReceiver(mInputToken);
+                wm.unregisterSurfaceControlInputReceiver(mSurfaceControl);
+                new SurfaceControl.Transaction().remove(mSurfaceControl).apply();
             }
         }
     }
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
index e5f8f47..7330ec1 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
@@ -50,10 +50,11 @@
     private static final String TAG = "SurfaceInputTestActivity";
     private SurfaceView mLocalSurfaceView;
     private SurfaceView mRemoteSurfaceView;
-    private IBinder mInputToken;
     private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
     private SurfaceControl mParentSurfaceControl;
 
+    private SurfaceControl mLocalSurfaceControl;
+
     private final ServiceConnection mConnection = new ServiceConnection() {
         // Called when the connection with the service is established
         public void onServiceConnected(ComponentName className, IBinder service) {
@@ -112,30 +113,33 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken);
+        if (mLocalSurfaceControl != null) {
+            getWindowManager().unregisterSurfaceControlInputReceiver(mLocalSurfaceControl);
+            new SurfaceControl.Transaction().remove(mLocalSurfaceControl).apply();
+        }
     }
 
     private void addLocalChildSurfaceControl(AttachedSurfaceControl attachedSurfaceControl) {
-        SurfaceControl surfaceControl = new SurfaceControl.Builder().setName("LocalSC")
+        mLocalSurfaceControl = new SurfaceControl.Builder().setName("LocalSC")
                 .setBufferSize(100, 100).build();
-        attachedSurfaceControl.buildReparentTransaction(surfaceControl)
-                .setVisibility(surfaceControl, true)
-                .setCrop(surfaceControl, new Rect(0, 0, 100, 100))
-                .setPosition(surfaceControl, 250, 1000)
-                .setLayer(surfaceControl, 1).apply();
+        attachedSurfaceControl.buildReparentTransaction(mLocalSurfaceControl)
+                .setVisibility(mLocalSurfaceControl, true)
+                .setCrop(mLocalSurfaceControl, new Rect(0, 0, 100, 100))
+                .setPosition(mLocalSurfaceControl, 250, 1000)
+                .setLayer(mLocalSurfaceControl, 1).apply();
 
         Paint paint = new Paint();
         paint.setColor(Color.WHITE);
         paint.setTextSize(20);
 
-        Surface surface = new Surface(surfaceControl);
+        Surface surface = new Surface(mLocalSurfaceControl);
         Canvas c = surface.lockCanvas(null);
         c.drawColor(Color.GREEN);
         c.drawText("Local SC", 0, 0, paint);
         surface.unlockCanvasAndPost(c);
         WindowManager wm = getSystemService(WindowManager.class);
-        mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
-                attachedSurfaceControl.getHostToken(), surfaceControl,
+        wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
+                attachedSurfaceControl.getHostToken(), mLocalSurfaceControl,
                 Choreographer.getInstance(), event -> {
                     Log.d(TAG, "onInputEvent-sc " + event);
                     return false;
@@ -143,8 +147,6 @@
     }
 
     private final SurfaceHolder.Callback mLocalSurfaceViewCallback = new SurfaceHolder.Callback() {
-        private IBinder mInputToken;
-
         @Override
         public void surfaceCreated(@NonNull SurfaceHolder holder) {
             Paint paint = new Paint();
@@ -157,7 +159,7 @@
             holder.unlockCanvasAndPost(c);
 
             WindowManager wm = getSystemService(WindowManager.class);
-            mInputToken = wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
+            wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
                     mLocalSurfaceView.getHostToken(), mLocalSurfaceView.getSurfaceControl(),
                     Choreographer.getInstance(), event -> {
                         Log.d(TAG, "onInputEvent-local " + event);
@@ -173,9 +175,8 @@
 
         @Override
         public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
-            if (mInputToken != null) {
-                getWindowManager().unregisterSurfaceControlInputReceiver(mInputToken);
-            }
+            getWindowManager().unregisterSurfaceControlInputReceiver(
+                    mLocalSurfaceView.getSurfaceControl());
         }
     };
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 0b16e2c..d03f97e 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -415,6 +415,8 @@
   intent_filter_action["action"].Action(RequiredNameIsNotEmpty);
   intent_filter_action["category"].Action(RequiredNameIsNotEmpty);
   intent_filter_action["data"];
+  intent_filter_action["uri-relative-filter-group"];
+  intent_filter_action["uri-relative-filter-group"]["data"];
 
   // Common <meta-data> actions.
   xml::XmlNodeAction meta_data_action;