Merge "Add request to authentication entry pending intent"
diff --git a/Android.bp b/Android.bp
index 2a6afe5..cfab18e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -694,3 +694,12 @@
     "ProtoLibraries.bp",
     "TestProtoLibraries.bp",
 ]
+
+java_api_contribution {
+    name: "api-stubs-docs-non-updatable-public-stubs",
+    api_surface: "public",
+    api_file: "core/api/current.txt",
+    visibility: [
+        "//build/orchestrator/apis",
+    ],
+}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 48c44c9..fc046fb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -598,3 +598,12 @@
     ],
     visibility: ["//visibility:public"],
 }
+
+java_api_contribution {
+    name: "frameworks-base-core-api-module-lib-stubs",
+    api_surface: "module-lib",
+    api_file: "core/api/module-lib-current.txt",
+    visibility: [
+        "//build/orchestrator/apis",
+    ],
+}
diff --git a/apct-tests/perftests/packagemanager/AndroidManifest.xml b/apct-tests/perftests/packagemanager/AndroidManifest.xml
index 3b9431f..b4a34b8 100644
--- a/apct-tests/perftests/packagemanager/AndroidManifest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidManifest.xml
@@ -26,6 +26,7 @@
 
     <permission android:name="com.android.perftests.packagemanager.TestPermission" />
     <uses-permission android:name="com.android.perftests.packagemanager.TestPermission" />
+    <uses-permission android:name="android.permission.GET_APP_METADATA" />
 
     <queries>
         <package android:name="com.android.perftests.appenumeration0" />
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
index 673c6a3..4bcc8c4 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
@@ -19,10 +19,15 @@
 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
@@ -31,11 +36,19 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
+
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class PackageManagerPerfTest {
@@ -46,6 +59,8 @@
     private static final ComponentName TEST_ACTIVITY =
             new ComponentName("com.android.perftests.packagemanager",
                     "android.perftests.utils.PerfTestActivity");
+    private static final String TEST_FIELD = "test";
+    private static final String TEST_VALUE = "value";
 
     @Rule
     public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -53,9 +68,39 @@
     @Rule
     public final PlatformCompatChangeRule mPlatformCompatChangeRule =
             new PlatformCompatChangeRule();
+    @Rule
+    public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
+            InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.DELETE_PACKAGES);
+
+    final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private PackageInstaller mPackageInstaller;
 
     public PackageManagerPerfTest() throws PackageManager.NameNotFoundException {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
+    }
+
+    private void installTestApp(TestApp testApp) throws IOException, InterruptedException {
+        Install install = Install.single(testApp);
+        final int expectedSessionId = install.createSession();
+        PackageInstaller.Session session =
+                InstallUtils.openPackageInstallerSession(expectedSessionId);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(TEST_FIELD, TEST_VALUE);
+        session.setAppMetadata(bundle);
+        LocalIntentSender localIntentSender = new LocalIntentSender();
+        session.commit(localIntentSender.getIntentSender());
+        Intent intent = localIntentSender.getResult();
+        InstallUtils.assertStatusSuccess(intent);
+    }
+
+    private void uninstallTestApp(String packageName) throws InterruptedException {
+        LocalIntentSender localIntentSender = new LocalIntentSender();
+        IntentSender intentSender = localIntentSender.getIntentSender();
+        mPackageInstaller.uninstall(packageName, intentSender);
+        Intent intent = localIntentSender.getResult();
+        InstallUtils.assertStatusSuccess(intent);
     }
 
     @Before
@@ -65,6 +110,24 @@
     }
 
     @Test
+    public void testGetAppMetadata() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        installTestApp(TestApp.A1);
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TestApp.A1.getPackageName();
+
+        while (state.keepRunning()) {
+            PersistableBundle bundle = pm.getAppMetadata(packageName);
+            state.pauseTiming();
+            assertEquals(bundle.size(), 1);
+            assertEquals(bundle.getString(TEST_FIELD), TEST_VALUE);
+            state.resumeTiming();
+        }
+        uninstallTestApp(packageName);
+    }
+
+    @Test
     @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
     public void testCheckPermissionExists() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/core/api/current.txt b/core/api/current.txt
index 4f6ccff4..3cc5631 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5905,7 +5905,7 @@
     ctor public LocaleConfig(@NonNull android.content.Context);
     ctor public LocaleConfig(@NonNull android.os.LocaleList);
     method public int describeContents();
-    method @NonNull public static android.app.LocaleConfig fromResources(@NonNull android.content.Context);
+    method @NonNull public static android.app.LocaleConfig fromContextIgnoringOverride(@NonNull android.content.Context);
     method public int getStatus();
     method @Nullable public android.os.LocaleList getSupportedLocales();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -9231,15 +9231,18 @@
   }
 
   public final class CompanionDeviceManager {
+    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void addOnAssociationsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback);
     method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
     method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
     method @Deprecated public void disassociate(@NonNull String);
     method public void disassociate(int);
+    method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
     method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
     method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
+    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
     method public void requestNotificationAccess(android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void stopObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
@@ -9260,6 +9263,10 @@
     method public abstract void onFailure(@Nullable CharSequence);
   }
 
+  public static interface CompanionDeviceManager.OnAssociationsChangedListener {
+    method public void onAssociationsChanged(@NonNull java.util.List<android.companion.AssociationInfo>);
+  }
+
   public abstract class CompanionDeviceService extends android.app.Service {
     ctor public CompanionDeviceService();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
@@ -11876,6 +11883,7 @@
   public class PackageInstaller {
     method public void abandonSession(int);
     method public void checkInstallConstraints(@NonNull java.util.List<java.lang.String>, @NonNull android.content.pm.PackageInstaller.InstallConstraints, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.content.pm.PackageInstaller.InstallConstraintsResult>);
+    method public void commitSessionAfterInstallConstraintsAreMet(int, @NonNull android.content.IntentSender, @NonNull android.content.pm.PackageInstaller.InstallConstraints, long);
     method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
     method @Deprecated @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getActiveStagedSessions();
@@ -11920,6 +11928,7 @@
     field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
     field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
     field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
+    field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
     field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
     field public static final int STATUS_SUCCESS = 0; // 0x0
   }
@@ -33212,6 +33221,7 @@
     field public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
     field public static final String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+    field public static final String DISALLOW_CONFIG_DEFAULT_APPS = "disallow_config_default_apps";
     field public static final String DISALLOW_CONFIG_LOCALE = "no_config_locale";
     field public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
     field public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
@@ -46635,8 +46645,8 @@
     field public static final int DONE = -1; // 0xffffffff
   }
 
-  public static class SegmentFinder.DefaultSegmentFinder extends android.text.SegmentFinder {
-    ctor public SegmentFinder.DefaultSegmentFinder(@NonNull int[]);
+  public static class SegmentFinder.PrescribedSegmentFinder extends android.text.SegmentFinder {
+    ctor public SegmentFinder.PrescribedSegmentFinder(@NonNull int[]);
     method public int nextEndBoundary(@IntRange(from=0) int);
     method public int nextStartBoundary(@IntRange(from=0) int);
     method public int previousEndBoundary(@IntRange(from=0) int);
@@ -54598,14 +54608,6 @@
 
   public abstract class HandwritingGesture {
     method @Nullable public final String getFallbackText();
-    field public static final int GESTURE_TYPE_DELETE = 4; // 0x4
-    field public static final int GESTURE_TYPE_DELETE_RANGE = 64; // 0x40
-    field public static final int GESTURE_TYPE_INSERT = 2; // 0x2
-    field public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 16; // 0x10
-    field public static final int GESTURE_TYPE_NONE = 0; // 0x0
-    field public static final int GESTURE_TYPE_REMOVE_SPACE = 8; // 0x8
-    field public static final int GESTURE_TYPE_SELECT = 1; // 0x1
-    field public static final int GESTURE_TYPE_SELECT_RANGE = 32; // 0x20
     field public static final int GRANULARITY_CHARACTER = 2; // 0x2
     field public static final int GRANULARITY_WORD = 1; // 0x1
   }
@@ -55105,15 +55107,15 @@
   public final class TextBoundsInfo implements android.os.Parcelable {
     method public int describeContents();
     method @IntRange(from=0, to=125) public int getCharacterBidiLevel(int);
-    method @NonNull public android.graphics.RectF getCharacterBounds(int);
+    method @NonNull public void getCharacterBounds(int, @NonNull android.graphics.RectF);
     method public int getCharacterFlags(int);
-    method public int getEnd();
+    method public int getEndIndex();
     method @NonNull public android.text.SegmentFinder getGraphemeSegmentFinder();
     method @NonNull public android.text.SegmentFinder getLineSegmentFinder();
-    method @NonNull public android.graphics.Matrix getMatrix();
+    method @NonNull public void getMatrix(@NonNull android.graphics.Matrix);
     method public int getOffsetForPosition(float, float);
     method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy);
-    method public int getStart();
+    method public int getStartIndex();
     method @NonNull public android.text.SegmentFinder getWordSegmentFinder();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextBoundsInfo> CREATOR;
@@ -55124,7 +55126,7 @@
   }
 
   public static final class TextBoundsInfo.Builder {
-    ctor public TextBoundsInfo.Builder();
+    ctor public TextBoundsInfo.Builder(int, int);
     method @NonNull public android.view.inputmethod.TextBoundsInfo build();
     method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder clear();
     method @NonNull public android.view.inputmethod.TextBoundsInfo.Builder setCharacterBidiLevel(@NonNull int[]);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3ac8286..85f8813 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -806,10 +806,12 @@
     method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
     method @Nullable public String getDeliveryGroupMatchingKey();
     method public int getDeliveryGroupPolicy();
+    method public boolean isDeferUntilActive();
     method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
     method public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
+    method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
     method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
     method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
     method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
@@ -2973,18 +2975,11 @@
   }
 
   public final class CompanionDeviceManager {
-    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void addOnAssociationsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
     method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public void associate(@NonNull String, @NonNull android.net.MacAddress, @NonNull byte[]);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public java.util.List<android.companion.AssociationInfo> getAllAssociations();
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int);
     method @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int);
-    method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void removeOnAssociationsChangedListener(@NonNull android.companion.CompanionDeviceManager.OnAssociationsChangedListener);
-  }
-
-  public static interface CompanionDeviceManager.OnAssociationsChangedListener {
-    method public void onAssociationsChanged(@NonNull java.util.List<android.companion.AssociationInfo>);
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3bc11fa..d25c3f5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1246,7 +1246,7 @@
   public final class CameraManager {
     method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
     method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
-    field public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; // 0xef10e60L
+    field public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L; // 0xef10e60L
   }
 
   public abstract static class CameraManager.AvailabilityCallback {
@@ -3322,6 +3322,14 @@
 
   public abstract class HandwritingGesture {
     method public final int getGestureType();
+    field public static final int GESTURE_TYPE_DELETE = 4; // 0x4
+    field public static final int GESTURE_TYPE_DELETE_RANGE = 64; // 0x40
+    field public static final int GESTURE_TYPE_INSERT = 2; // 0x2
+    field public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 16; // 0x10
+    field public static final int GESTURE_TYPE_NONE = 0; // 0x0
+    field public static final int GESTURE_TYPE_REMOVE_SPACE = 8; // 0x8
+    field public static final int GESTURE_TYPE_SELECT = 1; // 0x1
+    field public static final int GESTURE_TYPE_SELECT_RANGE = 32; // 0x20
   }
 
   public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6826b67..3e21124 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -20,6 +20,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionMethod;
+import android.annotation.PermissionName;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager.ProcessCapability;
 import android.app.ActivityManager.RestrictionLevel;
@@ -31,8 +33,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PermissionMethod;
-import android.content.pm.PermissionName;
 import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Bundle;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 86482c0..3f27964 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4558,7 +4558,11 @@
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             if (!service.isUiContext()) { // WindowProviderService is a UI Context.
-                service.updateDeviceId(mLastReportedDeviceId);
+                VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
+                if (mLastReportedDeviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT
+                        || vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) {
+                    service.updateDeviceId(mLastReportedDeviceId);
+                }
             }
             service.onCreate();
             mServicesData.put(data.token, data);
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index e202760..5cf10d0 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -63,6 +63,7 @@
     private long mRequireCompatChangeId = CHANGE_INVALID;
     private boolean mRequireCompatChangeEnabled = true;
     private boolean mIsAlarmBroadcast = false;
+    private boolean mIsDeferUntilActive = false;
     private long mIdForResponseEvent;
     private @Nullable IntentFilter mRemoveMatchingFilter;
     private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
@@ -201,6 +202,12 @@
             "android:broadcast.removeMatchingFilter";
 
     /**
+     * Corresponds to {@link #setDeferUntilActive(boolean)}.
+     */
+    private static final String KEY_DEFER_UNTIL_ACTIVE =
+            "android:broadcast.deferuntilactive";
+
+    /**
      * Corresponds to {@link #setDeliveryGroupPolicy(int)}.
      */
     private static final String KEY_DELIVERY_GROUP_POLICY =
@@ -320,6 +327,7 @@
                 BundleMerger.class);
         mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
                 IntentFilter.class);
+        mIsDeferUntilActive = opts.getBoolean(KEY_DEFER_UNTIL_ACTIVE, false);
     }
 
     /**
@@ -700,6 +708,41 @@
     }
 
     /**
+     * Sets whether the broadcast should not run until the process is in an active process state
+     * (ie, a process exists for the app and the app is not in a cached process state).
+     *
+     * Whether an app's process state is considered active is independent of its standby bucket.
+     *
+     * A broadcast that is deferred until the process is active will not execute until the process
+     * is brought to an active state by some other action, like a job, alarm, or service binding. As
+     * a result, the broadcast may be delayed indefinitely. This deferral only applies to runtime
+     * registered receivers of a broadcast. Any manifest receivers will run immediately, similar to
+     * how a manifest receiver would start a new process in order to run a broadcast receiver.
+     *
+     * Ordered broadcasts, alarm broadcasts, interactive broadcasts, and manifest broadcasts are
+     * never deferred.
+     *
+     * Unordered broadcasts and unordered broadcasts with completion callbacks may be
+     * deferred. Completion callbacks for broadcasts deferred until active are
+     * best-effort. Completion callbacks will run when all eligible processes have finished
+     * executing the broadcast. Processes in inactive process states that defer the broadcast are
+     * not considered eligible and may not execute the broadcast prior to the completion callback.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) {
+        mIsDeferUntilActive = shouldDefer;
+        return this;
+    }
+
+    /** @hide */
+    @SystemApi
+    public boolean isDeferUntilActive() {
+        return mIsDeferUntilActive;
+    }
+
+    /**
      * When enqueuing this broadcast, remove all pending broadcasts previously
      * sent by this app which match the given filter.
      * <p>
@@ -963,6 +1006,9 @@
         if (mDeliveryGroupMatchingFilter != null) {
             b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter);
         }
+        if (mIsDeferUntilActive) {
+            b.putBoolean(KEY_DEFER_UNTIL_ACTIVE, mIsDeferUntilActive);
+        }
         return b.isEmpty() ? null : b;
     }
 }
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 50ba7db..69693fc 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -110,7 +110,7 @@
      * @see Context#createPackageContext(String, int).
      */
     @NonNull
-    public static LocaleConfig fromResources(@NonNull Context context) {
+    public static LocaleConfig fromContextIgnoringOverride(@NonNull Context context) {
         return new LocaleConfig(context, false);
     }
 
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 63f4bcf..d31124d 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -662,9 +662,7 @@
      * @return the associations list
      * @see #addOnAssociationsChangedListener(Executor, OnAssociationsChangedListener)
      * @see #removeOnAssociationsChangedListener(OnAssociationsChangedListener)
-     * @hide
      */
-    @SystemApi
     @UserHandleAware
     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public @NonNull List<AssociationInfo> getAllAssociations() {
@@ -678,10 +676,7 @@
 
     /**
      * Listener for any changes to {@link AssociationInfo}.
-     *
-     * @hide
      */
-    @SystemApi
     public interface OnAssociationsChangedListener {
         /**
          * Invoked when a change occurs to any of the associations for the user (including adding
@@ -696,9 +691,7 @@
      * Register listener for any changes to {@link AssociationInfo}.
      *
      * @see #getAllAssociations()
-     * @hide
      */
-    @SystemApi
     @UserHandleAware
     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public void addOnAssociationsChangedListener(
@@ -720,9 +713,7 @@
      * Unregister listener for any changes to {@link AssociationInfo}.
      *
      * @see #getAllAssociations()
-     * @hide
      */
-    @SystemApi
     @UserHandleAware
     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public void removeOnAssociationsChangedListener(
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 879c8bd..74c3f45 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -26,6 +26,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionMethod;
+import android.annotation.PermissionName;
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
 import android.annotation.StringRes;
@@ -53,8 +55,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PermissionMethod;
-import android.content.pm.PermissionName;
 import android.content.res.AssetManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7ee8f60..4318c39 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11601,7 +11601,7 @@
     private void toUriInner(StringBuilder uri, String scheme, String defAction,
             String defPackage, int flags) {
         if (scheme != null) {
-            uri.append("scheme=").append(scheme).append(';');
+            uri.append("scheme=").append(Uri.encode(scheme)).append(';');
         }
         if (mAction != null && !mAction.equals(defAction)) {
             uri.append("action=").append(Uri.encode(mAction)).append(';');
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index c9a5632..9dd9c0f 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -46,6 +46,8 @@
     void commit(in IntentSender statusReceiver, boolean forTransferred);
     void transfer(in String packageName);
     void abandon();
+    void seal();
+    List<String> fetchPackageNames();
 
     DataLoaderParamsParcel getDataLoaderParams();
     void addFile(int location, String name, long lengthBytes, in byte[] metadata, in byte[] signature);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 812b5b3..df1340d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -44,8 +44,11 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager.DeleteFlags;
@@ -59,9 +62,11 @@
 import android.icu.util.ULocale;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.FileBridge;
 import android.os.Handler;
 import android.os.HandlerExecutor;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
@@ -226,8 +231,8 @@
      * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
      * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
      * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
-     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
-     * {@link #STATUS_FAILURE_STORAGE}.
+     * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
+     * {@link #STATUS_FAILURE_STORAGE}, or {@link #STATUS_FAILURE_TIMEOUT}.
      * <p>
      * More information about a status may be available through additional
      * extras; see the individual status documentation for details.
@@ -441,6 +446,13 @@
     public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
 
     /**
+     * The operation failed because it didn't complete within the specified timeout.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_TIMEOUT = 8;
+
+    /**
      * Default value, non-streaming installation session.
      *
      * @see #EXTRA_DATA_LOADER_TYPE
@@ -1016,6 +1028,61 @@
     }
 
     /**
+     * Commit the session when all constraints are satisfied. This is a convenient method to
+     * combine {@link #waitForInstallConstraints(List, InstallConstraints, IntentSender, long)}
+     * and {@link Session#commit(IntentSender)}.
+     * <p>
+     * Once this method is called, the session is sealed and no additional mutations
+     * may be performed on the session. In the case of timeout, you may commit the
+     * session again using this method or {@link Session#commit(IntentSender)} for retries.
+     *
+     * @param statusReceiver Called when the state of the session changes. Intents
+     *                       sent to this receiver contain {@link #EXTRA_STATUS}.
+     *                       Refer to the individual status codes on how to handle them.
+     * @param constraints The requirements to satisfy before committing the session.
+     * @param timeoutMillis The maximum time to wait, in milliseconds until the
+     *                      constraints are satisfied. The caller will be notified via
+     *                      {@code statusReceiver} if timeout happens before commit.
+     */
+    public void commitSessionAfterInstallConstraintsAreMet(int sessionId,
+            @NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints,
+            @DurationMillisLong long timeoutMillis) {
+        try {
+            var session = mInstaller.openSession(sessionId);
+            session.seal();
+            var packageNames = session.fetchPackageNames();
+            var intentSender = new IntentSender((IIntentSender) new IIntentSender.Stub() {
+                @Override
+                public void send(int code, Intent intent, String resolvedType,
+                        IBinder allowlistToken, IIntentReceiver finishedReceiver,
+                        String requiredPermission, Bundle options)  {
+                    var result = intent.getParcelableExtra(
+                            PackageInstaller.EXTRA_INSTALL_CONSTRAINTS_RESULT,
+                            InstallConstraintsResult.class);
+                    try {
+                        if (result.isAllConstraintsSatisfied()) {
+                            session.commit(statusReceiver, false);
+                        } else {
+                            // timeout
+                            final Intent fillIn = new Intent();
+                            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+                            fillIn.putExtra(PackageInstaller.EXTRA_STATUS, STATUS_FAILURE_TIMEOUT);
+                            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+                                    "Install constraints not satisfied within timeout");
+                            statusReceiver.sendIntent(
+                                    ActivityThread.currentApplication(), 0, fillIn, null, null);
+                        }
+                    } catch (Exception ignore) {
+                    }
+                }
+            });
+            waitForInstallConstraints(packageNames, constraints, intentSender, timeoutMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Events for observing session lifecycle.
      * <p>
      * A typical session lifecycle looks like this:
diff --git a/core/java/android/content/pm/PermissionMethod.java b/core/java/android/content/pm/PermissionMethod.java
deleted file mode 100644
index 647c696..0000000
--- a/core/java/android/content/pm/PermissionMethod.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2022 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.pm;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Documents that the subject method's job is to look
- * up whether the provided or calling uid/pid has the requested permission.
- *
- * <p>Methods should either return `void`, but potentially throw {@link SecurityException},
- * or return {@link android.content.pm.PackageManager.PermissionResult} `int`.
- *
- * @hide
- */
-@Retention(CLASS)
-@Target({METHOD})
-public @interface PermissionMethod {
-    /**
-     * Hard-coded list of permissions checked by this method
-     */
-    @PermissionName String[] value() default {};
-    /**
-     * If true, the check passes if the caller
-     * has any ONE of the supplied permissions
-     */
-    boolean anyOf() default false;
-    /**
-     * Signifies that the permission check passes if
-     * the calling process OR the current process has
-     * the permission
-     */
-    boolean orSelf() default false;
-}
diff --git a/core/java/android/content/pm/PermissionName.java b/core/java/android/content/pm/PermissionName.java
deleted file mode 100644
index 719e13b..0000000
--- a/core/java/android/content/pm/PermissionName.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 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.pm;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that the annotated {@link String} represents a permission name.
- *
- * @hide
- */
-@Retention(CLASS)
-@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
-public @interface PermissionName {}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index c716f319..81d6ba9 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -24,7 +24,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
-import android.app.compat.CompatChanges;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.ImageFormat;
@@ -42,7 +41,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.renderscript.Allocation;
 import android.renderscript.Element;
 import android.renderscript.RSIllegalArgumentException;
@@ -279,14 +277,6 @@
      */
     public native static int getNumberOfCameras();
 
-    private static final boolean sLandscapeToPortrait =
-            SystemProperties.getBoolean(CameraManager.LANDSCAPE_TO_PORTRAIT_PROP, false);
-
-    private static boolean shouldOverrideToPortrait() {
-        return CompatChanges.isChangeEnabled(CameraManager.OVERRIDE_FRONT_CAMERA_APP_COMPAT)
-                && sLandscapeToPortrait;
-    }
-
     /**
      * Returns the information about a particular camera.
      * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1.
@@ -296,7 +286,8 @@
      *    low-level failure).
      */
     public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) {
-        boolean overrideToPortrait = shouldOverrideToPortrait();
+        boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait(
+                ActivityThread.currentApplication().getApplicationContext());
 
         _getCameraInfo(cameraId, overrideToPortrait, cameraInfo);
         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
@@ -493,7 +484,8 @@
             mEventHandler = null;
         }
 
-        boolean overrideToPortrait = shouldOverrideToPortrait();
+        boolean overrideToPortrait = CameraManager.shouldOverrideToPortrait(
+                ActivityThread.currentApplication().getApplicationContext());
         return native_setup(new WeakReference<Camera>(this), cameraId,
                 ActivityThread.currentOpPackageName(), overrideToPortrait);
     }
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 5b6e288..b5a58b2 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -115,7 +115,14 @@
     @Overridable
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
     @TestApi
-    public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L;
+    public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L;
+
+    /**
+     * Package-level opt in/out for the above.
+     * @hide
+     */
+    public static final String PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT =
+            "android.camera.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT";
 
     /**
      * System property for allowing the above
@@ -607,7 +614,7 @@
             try {
                 Size displaySize = getDisplaySize();
 
-                boolean overrideToPortrait = shouldOverrideToPortrait();
+                boolean overrideToPortrait = shouldOverrideToPortrait(mContext);
                 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
                         mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait);
                 try {
@@ -727,7 +734,7 @@
                         "Camera service is currently unavailable");
                 }
 
-                boolean overrideToPortrait = shouldOverrideToPortrait();
+                boolean overrideToPortrait = shouldOverrideToPortrait(mContext);
                 cameraUser = cameraService.connectDevice(callbacks, cameraId,
                     mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
                     oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion,
@@ -1159,9 +1166,26 @@
         return CameraManagerGlobal.get().getTorchStrengthLevel(cameraId);
     }
 
-    private static boolean shouldOverrideToPortrait() {
-        return CompatChanges.isChangeEnabled(OVERRIDE_FRONT_CAMERA_APP_COMPAT)
-                && CameraManagerGlobal.sLandscapeToPortrait;
+    /**
+     * @hide
+     */
+    public static boolean shouldOverrideToPortrait(@Nullable Context context) {
+        if (!CameraManagerGlobal.sLandscapeToPortrait) {
+            return false;
+        }
+
+        if (context != null) {
+            PackageManager packageManager = context.getPackageManager();
+
+            try {
+                return packageManager.getProperty(context.getOpPackageName(),
+                            PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+            } catch (PackageManager.NameNotFoundException e) {
+                // No such property
+            }
+        }
+
+        return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT);
     }
 
     /**
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index dba1a5e..6a667fe 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -251,6 +251,10 @@
         @Nullable
         private Boolean lastResult;
 
+        public FoldStateListener(Context context) {
+            this(context, folded -> {});
+        }
+
         public FoldStateListener(Context context, Consumer<Boolean> listener) {
             mFoldedDeviceStates = context.getResources().getIntArray(
                     com.android.internal.R.array.config_foldedDeviceStates);
@@ -266,5 +270,10 @@
                 mDelegate.accept(folded);
             }
         }
+
+        @Nullable
+        public Boolean getFolded() {
+            return lastResult;
+        }
     }
 }
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 2bf187a..5fcc31e 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -43,7 +43,7 @@
     byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
 
     // Retrieve static sensor properties for all face sensors
-    @EnforcePermission("MANAGE_BIOMETRIC")
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
 
     // Retrieve static sensor properties for the specified sensor
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 8759a6a..8e6f6dc 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -767,20 +767,20 @@
     /**
      * Invokes {@link IRemoteInputConnection#requestTextBoundsInfo(InputConnectionCommandHeader,
      * RectF, ResultReceiver)}
-     * @param rectF {@code rectF} parameter to be passed.
+     * @param bounds {@code rectF} parameter to be passed.
      * @param executor {@code Executor} parameter to be passed.
      * @param consumer {@code Consumer} parameter to be passed.
      */
     @AnyThread
     public void requestTextBoundsInfo(
-            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<TextBoundsInfoResult> consumer) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(consumer);
 
         final ResultReceiver resultReceiver = new TextBoundsInfoResultReceiver(executor, consumer);
         try {
-            mConnection.requestTextBoundsInfo(createHeader(), rectF, resultReceiver);
+            mConnection.requestTextBoundsInfo(createHeader(), bounds, resultReceiver);
         } catch (RemoteException e) {
             executor.execute(() -> consumer.accept(new TextBoundsInfoResult(CODE_CANCELLED)));
         }
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index f93f9ab..ec26ace 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -476,9 +476,9 @@
 
     @AnyThread
     public void requestTextBoundsInfo(
-            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<TextBoundsInfoResult> consumer) {
-        mInvoker.requestTextBoundsInfo(rectF, executor, consumer);
+        mInvoker.requestTextBoundsInfo(bounds, executor, consumer);
     }
 
     @AnyThread
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index def0cbd..00676f3 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -358,16 +358,16 @@
      * Return the Linux UID assigned to the process that sent the transaction
      * currently being processed.
      *
-     * Logs WTF if the current thread is not currently
+     * Slog.wtf if the current thread is not currently
      * executing an incoming transaction and the calling identity has not been
      * explicitly set with {@link #clearCallingIdentity()}
      *
      * @hide
      */
-    public static final int getCallingUidOrWtf() {
+    public static final int getCallingUidOrWtf(String message) {
         if (!isDirectlyHandlingTransaction() && !hasExplicitIdentity()) {
-            Log.wtfStack(TAG,
-                    "Thread is not in a binder transaction, "
+            Slog.wtf(TAG,
+                    message + ": Thread is not in a binder transaction, "
                             + "and the calling identity has not been "
                             + "explicitly set with clearCallingIdentity");
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0e4c2b2..b016c781 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1472,6 +1472,21 @@
     public static final String DISALLOW_BIOMETRIC = "disallow_biometric";
 
     /**
+     * Specifies whether the user is allowed to modify default apps in settings.
+     *
+     * <p>This restriction can be set by device or profile owner.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_DEFAULT_APPS = "disallow_config_default_apps";
+
+    /**
      * Application restriction key that is used to indicate the pending arrival
      * of real restrictions for the app.
      *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 190b738..ec3ef9d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -22,6 +22,8 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionMethod;
+import android.annotation.PermissionName;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -48,7 +50,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PermissionName;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -9986,11 +9987,10 @@
                 "fingerprint_side_fps_auth_downtime";
 
         /**
-         * Whether or not a SFPS device is required to be interactive for auth to unlock the device.
+         * Whether or not a SFPS device is enabling the performant auth setting.
          * @hide
          */
-        public static final String SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED =
-                "sfps_require_screen_on_to_auth_enabled";
+        public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled";
 
         /**
          * Whether or not debugging is enabled.
@@ -18503,6 +18503,8 @@
          * @see #checkCallingPermission
          * @hide
          */
+        @PermissionMethod(orSelf = true)
+        @PackageManager.PermissionResult
         public static int checkCallingOrSelfPermission(@NonNull @PermissionName String permission) {
             return ActivityThread.currentApplication()
                .getApplicationContext().checkCallingOrSelfPermission(permission);
diff --git a/core/java/android/security/rkp/IRegistration.aidl b/core/java/android/security/rkp/IRegistration.aidl
index 6522a45..8ec13b9 100644
--- a/core/java/android/security/rkp/IRegistration.aidl
+++ b/core/java/android/security/rkp/IRegistration.aidl
@@ -17,6 +17,7 @@
 package android.security.rkp;
 
 import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.IStoreUpgradedKeyCallback;
 
 /**
  * This interface is associated with the registration of an
@@ -70,16 +71,18 @@
      * mechanism, see the documentation for IKeyMintDevice.upgradeKey.
      *
      * Once a key has been upgraded, the IRegistration where the key is stored
-     * needs to be told about the new blob. After calling storeUpgradedKey,
+     * needs to be told about the new blob. After calling storeUpgradedKeyAsync,
      * getKey will return the new key blob instead of the old one.
      *
      * Note that this function does NOT extend the lifetime of key blobs. The
      * certificate for the key is unchanged, and the key will still expire at
-     * the same time it would have if storeUpgradedKey had never been called.
+     * the same time it would have if storeUpgradedKeyAsync had never been called.
      *
      * @param oldKeyBlob The old key blob to be replaced by {@code newKeyBlob}.
-     *
      * @param newKeyblob The new blob to replace {@code oldKeyBlob}.
+     * @param callback Receives the result of the call. A callback must only
+     * be used with one {@code storeUpgradedKeyAsync} call at a time.
      */
-    void storeUpgradedKey(in byte[] oldKeyBlob, in byte[] newKeyBlob);
+    void storeUpgradedKeyAsync(
+            in byte[] oldKeyBlob, in byte[] newKeyBlob, IStoreUpgradedKeyCallback callback);
 }
diff --git a/core/java/android/security/rkp/IStoreUpgradedKeyCallback.aidl b/core/java/android/security/rkp/IStoreUpgradedKeyCallback.aidl
new file mode 100644
index 0000000..7f72fa0
--- /dev/null
+++ b/core/java/android/security/rkp/IStoreUpgradedKeyCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.security.rkp;
+
+/**
+ * Callback interface for storing an upgraded remotely provisioned key blob.
+ * {@link IRegistration}.
+ *
+ * @hide
+ */
+oneway interface IStoreUpgradedKeyCallback {
+    /**
+     * Called in response to {@link IRegistration.storeUpgradedKeyAsync}, indicating
+     * a remotely-provisioned key is available.
+     */
+    void onSuccess();
+
+    /**
+     * Called when an error has occurred while trying to store an upgraded
+     * remotely provisioned key.
+     *
+     * @param error A description of what failed, suitable for logging.
+     */
+    void onError(String error);
+}
diff --git a/core/java/android/text/SegmentFinder.java b/core/java/android/text/SegmentFinder.java
index be0094b..047d07a 100644
--- a/core/java/android/text/SegmentFinder.java
+++ b/core/java/android/text/SegmentFinder.java
@@ -74,7 +74,7 @@
     /**
      * The default {@link SegmentFinder} implementation based on given segment ranges.
      */
-    public static class DefaultSegmentFinder extends SegmentFinder {
+    public static class PrescribedSegmentFinder extends SegmentFinder {
         private final int[] mSegments;
 
         /**
@@ -87,7 +87,7 @@
          * @throws IllegalArgumentException if the given segments array's length is not even; the
          * given segments are not sorted or there are segments overlap with others.
          */
-        public DefaultSegmentFinder(@NonNull int[] segments) {
+        public PrescribedSegmentFinder(@NonNull int[] segments) {
             checkSegmentsValid(segments);
             mSegments = segments;
         }
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
index 2516269..e0fc426 100644
--- a/core/java/android/view/inputmethod/HandwritingGesture.java
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -82,38 +82,60 @@
     @IntDef({GRANULARITY_CHARACTER, GRANULARITY_WORD})
     @interface Granularity {}
 
-    /** Undefined gesture type. */
+    /**
+     * Undefined gesture type.
+     * @hide
+     */
+    @TestApi
     public static final int GESTURE_TYPE_NONE = 0x0000;
 
     /**
      * Gesture of type {@link SelectGesture} to select an area of text.
+     * @hide
      */
+    @TestApi
     public static final int GESTURE_TYPE_SELECT = 0x0001;
 
     /**
      * Gesture of type {@link InsertGesture} to insert text at a designated point.
+     * @hide
      */
+    @TestApi
     public static final int GESTURE_TYPE_INSERT = 1 << 1;
 
     /**
      * Gesture of type {@link DeleteGesture} to delete an area of text.
+     * @hide
      */
+    @TestApi
     public static final int GESTURE_TYPE_DELETE = 1 << 2;
 
-    /** Gesture of type {@link RemoveSpaceGesture} to remove whitespace from text. */
+    /**
+     * Gesture of type {@link RemoveSpaceGesture} to remove whitespace from text.
+     * @hide
+     */
+    @TestApi
     public static final int GESTURE_TYPE_REMOVE_SPACE = 1 << 3;
 
-    /** Gesture of type {@link JoinOrSplitGesture} to join or split text. */
+    /**
+     * Gesture of type {@link JoinOrSplitGesture} to join or split text.
+     * @hide
+     */
+    @TestApi
     public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 1 << 4;
 
     /**
      * Gesture of type {@link SelectRangeGesture} to select range of text.
+     * @hide
      */
+    @TestApi
     public static final int GESTURE_TYPE_SELECT_RANGE = 1 << 5;
 
     /**
      * Gesture of type {@link DeleteRangeGesture} to delete range of text.
+     * @hide
      */
+    @TestApi
     public static final int GESTURE_TYPE_DELETE_RANGE = 1 << 6;
 
     /**
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 9b519c3..6872536 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1262,13 +1262,13 @@
 
     /**
      * Called by input method to request the {@link TextBoundsInfo} for a range of text which is
-     * covered by or in vicinity of the given {@code RectF}. It can be used as a supplementary
+     * covered by or in vicinity of the given {@code bounds}. It can be used as a supplementary
      * method to implement the handwriting gesture API -
      * {@link #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)}.
      *
      * <p><strong>Editor authors</strong>: It's preferred that the editor returns a
      * {@link TextBoundsInfo} of all the text lines whose bounds intersect with the given
-     * {@code rectF}.
+     * {@code bounds}.
      * </p>
      *
      * <p><strong>IME authors</strong>: This method is expensive when the text is long. Please
@@ -1276,7 +1276,7 @@
      * consuming. It's preferable to only request text bounds in smaller areas.
      * </p>
      *
-     * @param rectF the interested area where the text bounds are requested, in the screen
+     * @param bounds the interested area where the text bounds are requested, in the screen
      *              coordinates.
      * @param executor the executor to run the callback.
      * @param consumer the callback invoked by editor to return the result. It must return a
@@ -1286,7 +1286,7 @@
      * @see android.view.inputmethod.TextBoundsInfoResult
      */
     default void requestTextBoundsInfo(
-            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<TextBoundsInfoResult> consumer) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(consumer);
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 4befd6f..5e323fa 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -362,9 +362,9 @@
      */
     @Override
     public void requestTextBoundsInfo(
-            @NonNull RectF rectF, @NonNull @CallbackExecutor Executor executor,
+            @NonNull RectF bounds, @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<TextBoundsInfoResult> consumer) {
-        mTarget.requestTextBoundsInfo(rectF, executor, consumer);
+        mTarget.requestTextBoundsInfo(bounds, executor, consumer);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 7525d72..6f8b422 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -1106,7 +1106,7 @@
     @Dispatching(cancellable = true)
     @Override
     public void requestTextBoundsInfo(
-            InputConnectionCommandHeader header, RectF rectF,
+            InputConnectionCommandHeader header, RectF bounds,
             @NonNull ResultReceiver resultReceiver) {
         dispatchWithTracing("requestTextBoundsInfo", () -> {
             if (header.mSessionId != mCurrentSessionId.get()) {
@@ -1121,7 +1121,7 @@
             }
 
             ic.requestTextBoundsInfo(
-                    rectF,
+                    bounds,
                     Runnable::run,
                     (textBoundsInfoResult) -> {
                         final int resultCode = textBoundsInfoResult.getResultCode();
diff --git a/core/java/android/view/inputmethod/TextBoundsInfo.java b/core/java/android/view/inputmethod/TextBoundsInfo.java
index dd05543..d42d94e 100644
--- a/core/java/android/view/inputmethod/TextBoundsInfo.java
+++ b/core/java/android/view/inputmethod/TextBoundsInfo.java
@@ -43,8 +43,8 @@
  * The text bounds information of a slice of text in the editor.
  *
  * <p> This class provides IME the layout information of the text within the range from
- * {@link #getStart()} to {@link #getEnd()}. It's intended to be used by IME as a supplementary API
- * to support handwriting gestures.
+ * {@link #getStartIndex()} to {@link #getEndIndex()}. It's intended to be used by IME as a
+ * supplementary API to support handwriting gestures.
  * </p>
  */
 public final class TextBoundsInfo implements Parcelable {
@@ -168,16 +168,13 @@
     private final SegmentFinder mGraphemeSegmentFinder;
 
     /**
-     * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
+     * Set the given {@link android.graphics.Matrix} to be the transformation
      * matrix that is to be applied other positional data in this class.
-     *
-     * @return a new instance (copy) of the transformation matrix.
      */
     @NonNull
-    public Matrix getMatrix() {
-        final Matrix matrix = new Matrix();
+    public void getMatrix(@NonNull Matrix matrix) {
+        Objects.requireNonNull(matrix);
         matrix.setValues(mMatrixValues);
-        return matrix;
     }
 
     /**
@@ -186,7 +183,7 @@
      *
      * @see Builder#setStartAndEnd(int, int)
      */
-    public int getStart() {
+    public int getStartIndex() {
         return mStart;
     }
 
@@ -196,28 +193,28 @@
      *
      * @see Builder#setStartAndEnd(int, int)
      */
-    public int getEnd() {
+    public int getEndIndex() {
         return mEnd;
     }
 
     /**
-     * Return the bounds of the character at the given {@code index}, in the coordinates of the
-     * editor.
+     * Set the bounds of the character at the given {@code index} to the given {@link RectF}, in
+     * the coordinates of the editor.
      *
      * @param index the index of the queried character.
-     * @return the bounding box of the queried character.
+     * @param bounds the {@link RectF} used to receive the result.
      *
      * @throws IndexOutOfBoundsException if the given {@code index} is out of the range from
      * the {@code start} to the {@code end}.
      */
     @NonNull
-    public RectF getCharacterBounds(int index) {
+    public void getCharacterBounds(int index, @NonNull RectF bounds) {
         if (index < mStart || index >= mEnd) {
             throw new IndexOutOfBoundsException("Index is out of the bounds of "
                     + "[" + mStart + ", " + mEnd + ").");
         }
         final int offset = 4 * (index - mStart);
-        return new RectF(mCharacterBounds[offset], mCharacterBounds[offset + 1],
+        bounds.set(mCharacterBounds[offset], mCharacterBounds[offset + 1],
                 mCharacterBounds[offset + 2], mCharacterBounds[offset + 3]);
     }
 
@@ -333,6 +330,16 @@
      * won't check the text in the ranges of [5, 7) and [12, 15).
      * </p>
      *
+     * <p> Under the following conditions, this method will return -1 indicating that no valid
+     * character is found:
+     * <ul>
+     *   <li> The given {@code y} coordinate is above the first line or below the last line (the
+     *   first line or the last line is identified by the {@link SegmentFinder} returned from
+     *   {@link #getLineSegmentFinder()}). </li>
+     *   <li> There is no character in this {@link TextBoundsInfo}. </li>
+     * </ul>
+     * </p>
+     *
      * @param x the x coordinates of the interested location, in the editor's coordinates.
      * @param y the y coordinates of the interested location, in the editor's coordinates.
      * @return the index of the character whose position is closest to the given location. It will
@@ -990,8 +997,8 @@
     public static final class Builder {
         private final float[] mMatrixValues = new float[9];
         private boolean mMatrixInitialized;
-        private int mStart;
-        private int mEnd;
+        private int mStart = -1;
+        private int mEnd = -1;
         private float[] mCharacterBounds;
         private int[] mCharacterFlags;
         private int[] mCharacterBidiLevels;
@@ -999,6 +1006,17 @@
         private SegmentFinder mWordSegmentFinder;
         private SegmentFinder mGraphemeSegmentFinder;
 
+        /**
+         * Create a builder for {@link TextBoundsInfo}.
+         * @param start the start index of the {@link TextBoundsInfo}, inclusive.
+         * @param end the end index of the {@link TextBoundsInfo}, exclusive.
+         * @throws IllegalArgumentException if the given {@code start} or {@code end} is negative,
+         * or {@code end} is smaller than the {@code start}.
+         */
+        public Builder(int start, int end) {
+            setStartAndEnd(start, end);
+        }
+
         /** Clear all the parameters set on this {@link Builder} to reuse it. */
         @NonNull
         public Builder clear() {
@@ -1152,7 +1170,7 @@
          *
          * @see #getGraphemeSegmentFinder()
          * @see SegmentFinder
-         * @see SegmentFinder.DefaultSegmentFinder
+         * @see SegmentFinder.PrescribedSegmentFinder
          */
         @NonNull
         public Builder setGraphemeSegmentFinder(@NonNull SegmentFinder graphemeSegmentFinder) {
@@ -1171,7 +1189,7 @@
          *
          * @see #getWordSegmentFinder()
          * @see SegmentFinder
-         * @see SegmentFinder.DefaultSegmentFinder
+         * @see SegmentFinder.PrescribedSegmentFinder
          */
         @NonNull
         public Builder setWordSegmentFinder(@NonNull SegmentFinder wordSegmentFinder) {
@@ -1193,7 +1211,7 @@
          *
          * @see #getLineSegmentFinder()
          * @see SegmentFinder
-         * @see SegmentFinder.DefaultSegmentFinder
+         * @see SegmentFinder.PrescribedSegmentFinder
          */
         @NonNull
         public Builder setLineSegmentFinder(@NonNull SegmentFinder lineSegmentFinder) {
@@ -1360,7 +1378,7 @@
                 breaks = GrowingArrayUtils.append(breaks, count++, start + offset);
             }
         }
-        return new SegmentFinder.DefaultSegmentFinder(Arrays.copyOf(breaks, count));
+        return new SegmentFinder.PrescribedSegmentFinder(Arrays.copyOf(breaks, count));
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b9b928e..4005bc8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -13245,7 +13245,7 @@
      * Creates the {@link TextBoundsInfo} for the text lines that intersects with the {@code rectF}.
      * @hide
      */
-    public TextBoundsInfo getTextBoundsInfo(@NonNull RectF rectF) {
+    public TextBoundsInfo getTextBoundsInfo(@NonNull RectF bounds) {
         final Layout layout = getLayout();
         if (layout == null) {
             // No valid text layout, return null.
@@ -13268,19 +13268,18 @@
         final float layoutLeft = viewportToContentHorizontalOffset();
         final float layoutTop = viewportToContentVerticalOffset();
 
-        final RectF localRectF = new RectF(rectF);
-        globalToLocalMatrix.mapRect(localRectF);
-        localRectF.offset(-layoutLeft, -layoutTop);
+        final RectF localBounds = new RectF(bounds);
+        globalToLocalMatrix.mapRect(localBounds);
+        localBounds.offset(-layoutLeft, -layoutTop);
 
         // Text length is 0. There is no character bounds, return empty TextBoundsInfo.
         // rectF doesn't intersect with the layout, return empty TextBoundsInfo.
-        if (!localRectF.intersects(0f, 0f, layout.getWidth(), layout.getHeight())
+        if (!localBounds.intersects(0f, 0f, layout.getWidth(), layout.getHeight())
                 || text.length() == 0) {
-            final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder();
+            final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder(0, 0);
             final SegmentFinder emptySegmentFinder =
-                    new SegmentFinder.DefaultSegmentFinder(new int[0]);
-            builder.setStartAndEnd(0, 0)
-                    .setMatrix(localToGlobalMatrix)
+                    new SegmentFinder.PrescribedSegmentFinder(new int[0]);
+            builder.setMatrix(localToGlobalMatrix)
                     .setCharacterBounds(new float[0])
                     .setCharacterBidiLevel(new int[0])
                     .setCharacterFlags(new int[0])
@@ -13290,8 +13289,8 @@
             return  builder.build();
         }
 
-        final int startLine = layout.getLineForVertical((int) Math.floor(localRectF.top));
-        final int endLine = layout.getLineForVertical((int) Math.floor(localRectF.bottom));
+        final int startLine = layout.getLineForVertical((int) Math.floor(localBounds.top));
+        final int endLine = layout.getLineForVertical((int) Math.floor(localBounds.bottom));
         final int start = layout.getLineStart(startLine);
         final int end = layout.getLineEnd(endLine);
 
@@ -13349,18 +13348,18 @@
             lineRanges[2 * offset] = layout.getLineStart(line);
             lineRanges[2 * offset + 1] = layout.getLineEnd(line);
         }
-        final SegmentFinder lineSegmentFinder = new SegmentFinder.DefaultSegmentFinder(lineRanges);
+        final SegmentFinder lineSegmentFinder =
+                new SegmentFinder.PrescribedSegmentFinder(lineRanges);
 
-        final TextBoundsInfo.Builder builder = new TextBoundsInfo.Builder();
-        builder.setStartAndEnd(start, end)
+        return new TextBoundsInfo.Builder(start, end)
                 .setMatrix(localToGlobalMatrix)
                 .setCharacterBounds(characterBounds)
                 .setCharacterBidiLevel(characterBidiLevels)
                 .setCharacterFlags(characterFlags)
                 .setGraphemeSegmentFinder(graphemeSegmentFinder)
                 .setLineSegmentFinder(lineSegmentFinder)
-                .setWordSegmentFinder(wordSegmentFinder);
-        return  builder.build();
+                .setWordSegmentFinder(wordSegmentFinder)
+                .build();
     }
 
     /**
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 52e7471..8690e8d 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -271,9 +271,9 @@
 
     @Override
     public void requestTextBoundsInfo(
-            @NonNull RectF rectF, @Nullable @CallbackExecutor Executor executor,
+            @NonNull RectF bounds, @Nullable @CallbackExecutor Executor executor,
             @NonNull Consumer<TextBoundsInfoResult> consumer) {
-        final TextBoundsInfo textBoundsInfo = mTextView.getTextBoundsInfo(rectF);
+        final TextBoundsInfo textBoundsInfo = mTextView.getTextBoundsInfo(bounds);
         final int resultCode;
         if (textBoundsInfo != null) {
             resultCode = TextBoundsInfoResult.CODE_SUCCESS;
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index 65016c2..b375936 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -111,7 +111,7 @@
                 int cursorUpdateMode, int cursorUpdateFilter, int imeDisplayId,
                  in AndroidFuture future /* T=Boolean */);
 
-    void requestTextBoundsInfo(in InputConnectionCommandHeader header, in RectF rect,
+    void requestTextBoundsInfo(in InputConnectionCommandHeader header, in RectF bounds,
            in ResultReceiver resultReceiver /* T=TextBoundsInfoResult */);
 
     void commitContent(in InputConnectionCommandHeader header, in InputContentInfo inputContentInfo,
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 3d8982b..04b7239cb 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -187,8 +187,6 @@
     private int mNextHistoryTagIdx = 0;
     private int mNumHistoryTagChars = 0;
     private int mHistoryBufferLastPos = -1;
-    private int mActiveHistoryStates = 0xffffffff;
-    private int mActiveHistoryStates2 = 0xffffffff;
     private long mLastHistoryElapsedRealtimeMs = 0;
     private long mTrackRunningHistoryElapsedRealtimeMs = 0;
     private long mTrackRunningHistoryUptimeMs = 0;
@@ -362,8 +360,6 @@
         mNextHistoryTagIdx = 0;
         mNumHistoryTagChars = 0;
         mHistoryBufferLastPos = -1;
-        mActiveHistoryStates = 0xffffffff;
-        mActiveHistoryStates2 = 0xffffffff;
         if (mStepDetailsCalculator != null) {
             mStepDetailsCalculator.clear();
         }
@@ -1098,8 +1094,9 @@
      */
     public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs,
             int brightnessBin) {
-        mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_BRIGHTNESS_MASK)
-                | (brightnessBin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
+        mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin,
+                HistoryItem.STATE_BRIGHTNESS_SHIFT,
+                HistoryItem.STATE_BRIGHTNESS_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1108,8 +1105,9 @@
      */
     public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs,
             int signalLevel) {
-        mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
-                | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+        mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel,
+                HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT,
+                HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1117,8 +1115,9 @@
      * Records a device idle mode change event.
      */
     public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) {
-        mHistoryCur.states2 = (mHistoryCur.states2 & ~HistoryItem.STATE2_DEVICE_IDLE_MASK)
-                | (mode << HistoryItem.STATE2_DEVICE_IDLE_SHIFT);
+        mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode,
+                HistoryItem.STATE2_DEVICE_IDLE_SHIFT,
+                HistoryItem.STATE2_DEVICE_IDLE_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1130,13 +1129,15 @@
         mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag;
         if (state != -1) {
             mHistoryCur.states =
-                    (mHistoryCur.states & ~HistoryItem.STATE_PHONE_STATE_MASK)
-                            | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
+                    setBitField(mHistoryCur.states, state,
+                            HistoryItem.STATE_PHONE_STATE_SHIFT,
+                            HistoryItem.STATE_PHONE_STATE_MASK);
         }
         if (signalStrength != -1) {
             mHistoryCur.states =
-                    (mHistoryCur.states & ~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
-                            | (signalStrength << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
+                    setBitField(mHistoryCur.states, signalStrength,
+                            HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT,
+                            HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK);
         }
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
@@ -1146,8 +1147,9 @@
      */
     public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs,
             int dataConnectionType) {
-        mHistoryCur.states = (mHistoryCur.states & ~HistoryItem.STATE_DATA_CONNECTION_MASK)
-                | (dataConnectionType << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
+        mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType,
+                HistoryItem.STATE_DATA_CONNECTION_SHIFT,
+                HistoryItem.STATE_DATA_CONNECTION_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1157,8 +1159,9 @@
     public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs,
             int supplState) {
         mHistoryCur.states2 =
-                (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
-                        | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
+                setBitField(mHistoryCur.states2, supplState,
+                        HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT,
+                        HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1168,8 +1171,9 @@
     public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs,
             int strengthBin) {
         mHistoryCur.states2 =
-                (mHistoryCur.states2 & ~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
-                        | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
+                setBitField(mHistoryCur.states2, strengthBin,
+                        HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT,
+                        HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK);
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
 
@@ -1227,6 +1231,16 @@
         }
     }
 
+    private int setBitField(int bits, int value, int shift, int mask) {
+        int shiftedValue = value << shift;
+        if ((shiftedValue & ~mask) != 0) {
+            Slog.wtfStack(TAG, "Value " + Integer.toHexString(value)
+                    + " does not fit in the bit field: " + Integer.toHexString(mask));
+            shiftedValue &= mask;
+        }
+        return (bits & ~mask) | shiftedValue;
+    }
+
     /**
      * Writes the current history item to history.
      */
@@ -1260,8 +1274,8 @@
         }
 
         final long timeDiffMs = (mHistoryBaseTimeMs + elapsedRealtimeMs) - mHistoryLastWritten.time;
-        final int diffStates = mHistoryLastWritten.states ^ (cur.states & mActiveHistoryStates);
-        final int diffStates2 = mHistoryLastWritten.states2 ^ (cur.states2 & mActiveHistoryStates2);
+        final int diffStates = mHistoryLastWritten.states ^ cur.states;
+        final int diffStates2 = mHistoryLastWritten.states2 ^ cur.states2;
         final int lastDiffStates = mHistoryLastWritten.states ^ mHistoryLastLastWritten.states;
         final int lastDiffStates2 = mHistoryLastWritten.states2 ^ mHistoryLastLastWritten.states2;
         if (DEBUG) {
@@ -1274,9 +1288,9 @@
 
         recordTraceEvents(cur.eventCode, cur.eventTag);
         recordTraceCounters(mHistoryLastWritten.states,
-                cur.states & mActiveHistoryStates, BatteryStats.HISTORY_STATE_DESCRIPTIONS);
+                cur.states, BatteryStats.HISTORY_STATE_DESCRIPTIONS);
         recordTraceCounters(mHistoryLastWritten.states2,
-                cur.states2 & mActiveHistoryStates2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS);
+                cur.states2, BatteryStats.HISTORY_STATE2_DESCRIPTIONS);
 
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                 && timeDiffMs < 1000 && (diffStates & lastDiffStates) == 0
@@ -1387,8 +1401,6 @@
         final boolean hasTags = mHistoryLastWritten.tagsFirstOccurrence || cur.tagsFirstOccurrence;
         mHistoryLastWritten.setTo(mHistoryBaseTimeMs + elapsedRealtimeMs, cmd, cur);
         mHistoryLastWritten.tagsFirstOccurrence = hasTags;
-        mHistoryLastWritten.states &= mActiveHistoryStates;
-        mHistoryLastWritten.states2 &= mActiveHistoryStates2;
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryElapsedRealtimeMs = elapsedRealtimeMs;
         cur.wakelockTag = null;
@@ -1627,7 +1639,7 @@
         }
         if (cur.eventCode != HistoryItem.EVENT_NONE) {
             final int index = writeHistoryTag(cur.eventTag);
-            final int codeAndIndex = (cur.eventCode & 0xffff) | (index << 16);
+            final int codeAndIndex = setBitField(cur.eventCode & 0xffff, index, 16, 0xFFFF0000);
             dest.writeInt(codeAndIndex);
             if ((index & BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG) != 0) {
                 cur.eventTag.writeToParcel(dest, 0);
@@ -1689,9 +1701,11 @@
     }
 
     private int buildBatteryLevelInt(HistoryItem h) {
-        return ((((int) h.batteryLevel) << 25) & 0xfe000000)
-                | ((((int) h.batteryTemperature) << 15) & 0x01ff8000)
-                | ((((int) h.batteryVoltage) << 1) & 0x00007ffe);
+        int bits = 0;
+        bits = setBitField(bits, h.batteryLevel, 25, 0xfe000000 /* 7F << 25 */);
+        bits = setBitField(bits, h.batteryTemperature, 15, 0x01ff8000 /* 3FF << 15 */);
+        bits = setBitField(bits, h.batteryVoltage, 1, 0x00007ffe /* 3FFF << 1 */);
+        return bits;
     }
 
     private int buildStateInt(HistoryItem h) {
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index 82db716..cce1b2b 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -21,10 +21,15 @@
 import android.hardware.broadcastradio.ProgramInfo;
 import android.hardware.broadcastradio.ProgramListChunk;
 import android.hardware.broadcastradio.VendorKeyValue;
+import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioMetadata;
+import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.List;
 
 final class AidlTestUtils {
 
@@ -94,6 +99,14 @@
         return makeHalProgramInfo(hwSel, hwSel.primaryId, hwSel.primaryId, hwSignalQuality);
     }
 
+    static ProgramInfo programInfoToHalProgramInfo(RadioManager.ProgramInfo info) {
+        return makeHalProgramInfo(
+                ConversionUtils.programSelectorToHalProgramSelector(info.getSelector()),
+                ConversionUtils.identifierToHalProgramIdentifier(info.getLogicallyTunedTo()),
+                ConversionUtils.identifierToHalProgramIdentifier(info.getPhysicallyTunedTo()),
+                info.getSignalStrength());
+    }
+
     static ProgramInfo makeHalProgramInfo(
             android.hardware.broadcastradio.ProgramSelector hwSel,
             ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
@@ -108,7 +121,23 @@
         return hwInfo;
     }
 
-    static ProgramListChunk makeProgramListChunk(boolean purge, boolean complete,
+    static ProgramListChunk makeHalChunk(boolean purge, boolean complete,
+            List<RadioManager.ProgramInfo> modified, List<ProgramSelector.Identifier> removed) {
+        ProgramInfo[] halModified =
+                new android.hardware.broadcastradio.ProgramInfo[modified.size()];
+        for (int i = 0; i < modified.size(); i++) {
+            halModified[i] = programInfoToHalProgramInfo(modified.get(i));
+        }
+
+        ProgramIdentifier[] halRemoved =
+                new android.hardware.broadcastradio.ProgramIdentifier[removed.size()];
+        for (int i = 0; i < removed.size(); i++) {
+            halRemoved[i] = ConversionUtils.identifierToHalProgramIdentifier(removed.get(i));
+        }
+        return makeHalChunk(purge, complete, halModified, halRemoved);
+    }
+
+    static ProgramListChunk makeHalChunk(boolean purge, boolean complete,
             ProgramInfo[] modified, ProgramIdentifier[] removed) {
         ProgramListChunk halChunk = new ProgramListChunk();
         halChunk.purge = purge;
@@ -118,6 +147,21 @@
         return halChunk;
     }
 
+    static ProgramList.Chunk makeChunk(boolean purge, boolean complete,
+            List<RadioManager.ProgramInfo> modified,
+            List<ProgramSelector.Identifier> removed) throws RemoteException {
+        ArraySet<RadioManager.ProgramInfo> modifiedSet = new ArraySet<>();
+        if (modified != null) {
+            modifiedSet.addAll(modified);
+        }
+        ArraySet<ProgramSelector.Identifier> removedSet = new ArraySet<>();
+        if (removed != null) {
+            removedSet.addAll(removed);
+        }
+        ProgramList.Chunk chunk = new ProgramList.Chunk(purge, complete, modifiedSet, removedSet);
+        return chunk;
+    }
+
     static VendorKeyValue makeVendorKeyValue(String vendorKey, String vendorValue) {
         VendorKeyValue vendorKeyValue = new VendorKeyValue();
         vendorKeyValue.key = vendorKey;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 710c150..5d0e076 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -328,7 +328,7 @@
                 TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
         RadioManager.ProgramInfo dabInfo =
                 ConversionUtils.programInfoFromHalProgramInfo(halDabInfo);
-        ProgramListChunk halChunk = AidlTestUtils.makeProgramListChunk(purge, complete,
+        ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(purge, complete,
                 new ProgramInfo[]{halDabInfo},
                 new ProgramIdentifier[]{TEST_HAL_VENDOR_ID, TEST_HAL_FM_FREQUENCY_ID});
 
@@ -353,7 +353,7 @@
                         TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
         ProgramInfo halDabInfo = AidlTestUtils.makeHalProgramInfo(halDabSelector,
                 TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
-        ProgramListChunk halChunk = AidlTestUtils.makeProgramListChunk(purge, complete,
+        ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(purge, complete,
                 new ProgramInfo[]{halDabInfo}, new ProgramIdentifier[]{TEST_HAL_FM_FREQUENCY_ID});
 
         ProgramList.Chunk chunk = ConversionUtils.chunkFromHalProgramListChunk(halChunk);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
new file mode 100644
index 0000000..d54397e
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2022 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.broadcastradio.aidl;
+
+import android.hardware.broadcastradio.ProgramIdentifier;
+import android.hardware.broadcastradio.ProgramInfo;
+import android.hardware.broadcastradio.ProgramListChunk;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Unit tests for AIDL ProgramInfoCache
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ProgramInfoCacheTest {
+
+    private static final int TEST_SIGNAL_QUALITY = 90;
+
+    private static final ProgramSelector.Identifier TEST_FM_FREQUENCY_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+                    /* value= */ 88_500);
+    private static final RadioManager.ProgramInfo TEST_FM_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
+                    TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID, TEST_FM_FREQUENCY_ID,
+            TEST_SIGNAL_QUALITY);
+    private static final RadioManager.ProgramInfo TEST_FM_INFO_MODIFIED =
+            AidlTestUtils.makeProgramInfo(AidlTestUtils.makeProgramSelector(
+                    ProgramSelector.PROGRAM_TYPE_FM, TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID,
+                    TEST_FM_FREQUENCY_ID, /* signalQuality= */ 99);
+
+    private static final ProgramSelector.Identifier TEST_AM_FREQUENCY_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+                    /* value= */ 1_700);
+    private static final RadioManager.ProgramInfo TEST_AM_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
+                    TEST_AM_FREQUENCY_ID), TEST_AM_FREQUENCY_ID, TEST_AM_FREQUENCY_ID,
+            TEST_SIGNAL_QUALITY);
+
+    private static final ProgramSelector.Identifier TEST_RDS_PI_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI,
+                    /* value= */ 15_019);
+    private static final RadioManager.ProgramInfo TEST_RDS_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, TEST_RDS_PI_ID),
+            TEST_RDS_PI_ID, new ProgramSelector.Identifier(
+                    ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 89_500),
+            TEST_SIGNAL_QUALITY);
+
+    private static final ProgramSelector.Identifier TEST_DAB_DMB_SID_EXT_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
+                    /* value= */ 0xA000000111L);
+    private static final ProgramSelector.Identifier TEST_DAB_ENSEMBLE_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
+                    /* value= */ 0x1001);
+    private static final ProgramSelector.Identifier TEST_DAB_FREQUENCY_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
+                    /* value= */ 220_352);
+    private static final RadioManager.ProgramInfo TEST_DAB_INFO = AidlTestUtils.makeProgramInfo(
+            new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_DMB_SID_EXT_ID,
+                    new ProgramSelector.Identifier[]{TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID},
+                    /* vendorIds= */ null), TEST_DAB_DMB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID,
+            TEST_SIGNAL_QUALITY);
+
+    private static final ProgramSelector.Identifier TEST_VENDOR_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_VENDOR_START,
+                    /* value= */ 9_001);
+    private static final RadioManager.ProgramInfo TEST_VENDOR_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_VENDOR_START,
+                    TEST_VENDOR_ID), TEST_VENDOR_ID, TEST_VENDOR_ID, TEST_SIGNAL_QUALITY);
+
+    private static final ProgramInfoCache FULL_PROGRAM_INFO_CACHE = new ProgramInfoCache(
+            /* filter= */ null, /* complete= */ true,
+            TEST_FM_INFO, TEST_AM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO);
+
+    @Rule
+    public final Expect expect = Expect.create();
+
+    @Test
+    public void isComplete_forCompleteProgramInfoCache_returnsTrue() {
+        expect.withMessage("Complete program info cache")
+                .that(FULL_PROGRAM_INFO_CACHE.isComplete()).isTrue();
+    }
+
+    @Test
+    public void isComplete_forIncompleteProgramInfoCache_returnsFalse() {
+        ProgramInfoCache programInfoCache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false);
+        expect.withMessage("Incomplete program info cache")
+                .that(programInfoCache.isComplete()).isFalse();
+    }
+
+    @Test
+    public void getFilter_forProgramInfoCache() {
+        ProgramList.Filter fmFilter = new ProgramList.Filter(
+                Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        ProgramInfoCache fmProgramInfoCache = new ProgramInfoCache(fmFilter);
+
+        expect.withMessage("Program info cache filter")
+                .that(fmProgramInfoCache.getFilter()).isEqualTo(fmFilter);
+    }
+
+    @Test
+    public void updateFromHalProgramListChunk_withPurgingCompleteChunk() {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false, TEST_FM_INFO);
+        ProgramListChunk chunk = AidlTestUtils.makeHalChunk(/* purge= */ true, /* complete= */ true,
+                new ProgramInfo[]{AidlTestUtils.programInfoToHalProgramInfo(TEST_RDS_INFO),
+                        AidlTestUtils.programInfoToHalProgramInfo(TEST_VENDOR_INFO)},
+                new ProgramIdentifier[]{});
+
+        cache.updateFromHalProgramListChunk(chunk);
+
+        expect.withMessage("Program cache updated with purge-enabled and complete chunk")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_RDS_INFO, TEST_VENDOR_INFO);
+        expect.withMessage("Complete program cache").that(cache.isComplete()).isTrue();
+    }
+
+    @Test
+    public void updateFromHalProgramListChunk_withNonPurgingIncompleteChunk() {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_AM_INFO);
+        ProgramListChunk chunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ false,
+                new ProgramInfo[]{AidlTestUtils.programInfoToHalProgramInfo(TEST_FM_INFO_MODIFIED),
+                        AidlTestUtils.programInfoToHalProgramInfo(TEST_VENDOR_INFO)},
+                new ProgramIdentifier[]{ConversionUtils.identifierToHalProgramIdentifier(
+                        TEST_RDS_PI_ID)});
+
+        cache.updateFromHalProgramListChunk(chunk);
+
+        expect.withMessage("Program cache updated with non-purging and incomplete chunk")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO_MODIFIED, TEST_VENDOR_INFO, TEST_AM_INFO);
+        expect.withMessage("Incomplete program cache").that(cache.isComplete()).isFalse();
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withNullFilter() {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ true);
+
+        cache.filterAndUpdateFromInternal(FULL_PROGRAM_INFO_CACHE, /* purge= */ false);
+
+        expect.withMessage("Program cache filtered by null filter")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO, TEST_AM_INFO, TEST_RDS_INFO, TEST_DAB_INFO,
+                        TEST_VENDOR_INFO);
+    }
+
+    @Test
+    public void  filterAndUpdateFromInternal_withEmptyFilter() {
+        ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(new ArraySet<>(),
+                new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false));
+
+        cache.filterAndUpdateFromInternal(FULL_PROGRAM_INFO_CACHE, /* purge= */ false);
+
+        expect.withMessage("Program cache filtered by empty filter")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO, TEST_AM_INFO, TEST_RDS_INFO, TEST_DAB_INFO,
+                        TEST_VENDOR_INFO);
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withFilterByIdentifierType() {
+        ProgramInfoCache cache = new ProgramInfoCache(
+                new ProgramList.Filter(Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+                        ProgramSelector.IDENTIFIER_TYPE_RDS_PI), new ArraySet<>(),
+                        /* includeCategories= */ true, /* excludeModifications= */ false));
+
+        cache.filterAndUpdateFromInternal(FULL_PROGRAM_INFO_CACHE, /* purge= */ false);
+
+        expect.withMessage("Program cache filtered by identifier type")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO, TEST_AM_INFO, TEST_RDS_INFO);
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withFilterByIdentifier() {
+        ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(
+                new ArraySet<>(), Set.of(TEST_FM_FREQUENCY_ID, TEST_DAB_DMB_SID_EXT_ID),
+                /* includeCategories= */ true, /* excludeModifications= */ false));
+        int maxNumModifiedPerChunk = 2;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndUpdateFromInternal(
+                FULL_PROGRAM_INFO_CACHE, /* purge= */ true, maxNumModifiedPerChunk,
+                maxNumRemovedPerChunk);
+
+        expect.withMessage("Program cache filtered by identifier")
+                .that(cache.toProgramInfoList()).containsExactly(TEST_FM_INFO, TEST_DAB_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ true);
+        verifyChunkListComplete(programListChunks, FULL_PROGRAM_INFO_CACHE.isComplete());
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_FM_INFO,
+                TEST_DAB_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk);
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withFilterExcludingCategories() {
+        ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(new ArraySet<>(),
+                new ArraySet<>(), /* includeCategories= */ false,
+                /* excludeModifications= */ false));
+        int maxNumModifiedPerChunk = 3;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndUpdateFromInternal(
+                FULL_PROGRAM_INFO_CACHE, /* purge= */ false, maxNumModifiedPerChunk,
+                maxNumRemovedPerChunk);
+
+        expect.withMessage("Program cache filtered by excluding categories")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO, TEST_AM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ true);
+        verifyChunkListComplete(programListChunks, FULL_PROGRAM_INFO_CACHE.isComplete());
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_FM_INFO,
+                TEST_AM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk);
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withFilterExcludingModifications() {
+        ProgramList.Filter filterExcludingModifications = new ProgramList.Filter(new ArraySet<>(),
+                new ArraySet<>(), /* includeCategories= */ true,
+                /* excludeModifications= */ true);
+        ProgramInfoCache cache = new ProgramInfoCache(filterExcludingModifications,
+                /* complete= */ true, TEST_FM_INFO, TEST_RDS_INFO, TEST_AM_INFO, TEST_DAB_INFO);
+        ProgramInfoCache halCache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false,
+                TEST_FM_INFO_MODIFIED, TEST_VENDOR_INFO);
+        int maxNumModifiedPerChunk = 2;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndUpdateFromInternal(halCache,
+                /* purge= */ false, maxNumModifiedPerChunk, maxNumRemovedPerChunk);
+
+        expect.withMessage("Program cache filtered by excluding modifications")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO, TEST_VENDOR_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ false);
+        verifyChunkListComplete(programListChunks, halCache.isComplete());
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_VENDOR_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk, TEST_RDS_PI_ID,
+                TEST_AM_FREQUENCY_ID, TEST_DAB_DMB_SID_EXT_ID);
+    }
+
+    @Test
+    public void filterAndUpdateFromInternal_withPurge() {
+        ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(new ArraySet<>(),
+                new ArraySet<>(), /* includeCategories= */ true,
+                /* excludeModifications= */ false),
+                /* complete= */ true, TEST_FM_INFO, TEST_RDS_INFO);
+        ProgramInfoCache halCache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false,
+                TEST_FM_INFO_MODIFIED, TEST_DAB_INFO, TEST_VENDOR_INFO);
+        int maxNumModifiedPerChunk = 2;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndUpdateFromInternal(halCache,
+                /* purge= */ true, maxNumModifiedPerChunk, maxNumRemovedPerChunk);
+
+        expect.withMessage("Purged program cache").that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO_MODIFIED, TEST_DAB_INFO, TEST_VENDOR_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ true);
+        verifyChunkListComplete(programListChunks, halCache.isComplete());
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_FM_INFO_MODIFIED,
+                TEST_DAB_INFO, TEST_VENDOR_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk);
+    }
+
+    @Test
+    public void filterAndApplyChunkInternal_withPurgingIncompleteChunk() throws RemoteException {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false, TEST_FM_INFO, TEST_DAB_INFO);
+        ProgramList.Chunk chunk = AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ false,
+                List.of(TEST_FM_INFO_MODIFIED, TEST_RDS_INFO, TEST_VENDOR_INFO),
+                List.of(TEST_DAB_DMB_SID_EXT_ID));
+        int maxNumModifiedPerChunk = 2;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(chunk,
+                maxNumModifiedPerChunk, maxNumRemovedPerChunk);
+
+        expect.withMessage("Program cache applied with non-purging and complete chunk")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO_MODIFIED, TEST_RDS_INFO, TEST_VENDOR_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ true);
+        verifyChunkListComplete(programListChunks, /* complete= */ false);
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_FM_INFO_MODIFIED,
+                TEST_RDS_INFO, TEST_VENDOR_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk);
+    }
+
+    @Test
+    public void filterAndApplyChunk_withNonPurgingCompleteChunk() throws RemoteException {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_AM_INFO, TEST_DAB_INFO);
+        ProgramList.Chunk chunk = AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                List.of(TEST_FM_INFO_MODIFIED, TEST_VENDOR_INFO),
+                List.of(TEST_RDS_PI_ID, TEST_AM_FREQUENCY_ID, TEST_DAB_DMB_SID_EXT_ID));
+        int maxNumModifiedPerChunk = 2;
+        int maxNumRemovedPerChunk = 2;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(chunk,
+                maxNumModifiedPerChunk, maxNumRemovedPerChunk);
+
+        expect.withMessage("Program cache applied with purge-enabled complete chunk")
+                .that(cache.toProgramInfoList())
+                .containsExactly(TEST_FM_INFO_MODIFIED, TEST_VENDOR_INFO);
+        verifyChunkListPurge(programListChunks, /* purge= */ false);
+        verifyChunkListComplete(programListChunks, /* complete= */ true);
+        verifyChunkListModified(programListChunks, maxNumModifiedPerChunk, TEST_FM_INFO_MODIFIED,
+                TEST_VENDOR_INFO);
+        verifyChunkListRemoved(programListChunks, maxNumRemovedPerChunk, TEST_RDS_PI_ID,
+                TEST_AM_FREQUENCY_ID, TEST_DAB_DMB_SID_EXT_ID);
+    }
+
+    private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) {
+        if (chunks.isEmpty()) {
+            return;
+        }
+        for (int i = 0; i < chunks.size(); i++) {
+            ProgramList.Chunk chunk = chunks.get(i);
+            boolean expectedPurge = (i == 0 && purge);
+
+            expect.withMessage("Purge for chunk %s", i)
+                    .that(chunk.isPurge()).isEqualTo(expectedPurge);
+        }
+    }
+
+    private void verifyChunkListComplete(List<ProgramList.Chunk> chunks, boolean complete) {
+        if (chunks.isEmpty()) {
+            return;
+        }
+        for (int i = 0; i < chunks.size(); i++) {
+            ProgramList.Chunk chunk = chunks.get(i);
+            boolean expectedComplete = (i == chunks.size() - 1 && complete);
+
+            expect.withMessage("Purge for chunk %s", i)
+                    .that(chunk.isComplete()).isEqualTo(expectedComplete);
+        }
+    }
+
+    private void verifyChunkListModified(List<ProgramList.Chunk> chunks,
+            int maxModifiedPerChunk, RadioManager.ProgramInfo... expectedProgramInfos) {
+        if (chunks.isEmpty()) {
+            expect.withMessage("Empty program info list")
+                    .that(expectedProgramInfos.length).isEqualTo(0);
+            return;
+        }
+
+        ArraySet<RadioManager.ProgramInfo> actualSet = new ArraySet<>();
+        for (int i = 0; i < chunks.size(); i++) {
+            Set<RadioManager.ProgramInfo> chunkModified = chunks.get(i).getModified();
+            actualSet.addAll(chunkModified);
+
+            expect.withMessage("Chunk %s modified program info array size", i)
+                    .that(chunkModified.size()).isAtMost(maxModifiedPerChunk);
+        }
+        expect.withMessage("Program info items")
+                .that(actualSet).containsExactlyElementsIn(expectedProgramInfos);
+    }
+
+    private void verifyChunkListRemoved(List<ProgramList.Chunk> chunks,
+            int maxRemovedPerChunk, ProgramSelector.Identifier... expectedIdentifiers) {
+        if (chunks.isEmpty()) {
+            expect.withMessage("Empty program info list")
+                    .that(expectedIdentifiers.length).isEqualTo(0);
+            return;
+        }
+
+        ArraySet<ProgramSelector.Identifier> actualSet = new ArraySet<>();
+        for (int i = 0; i < chunks.size(); i++) {
+            Set<ProgramSelector.Identifier> chunkRemoved = chunks.get(i).getRemoved();
+            actualSet.addAll(chunkRemoved);
+
+            expect.withMessage("Chunk %s removed identifier array size ", i)
+                    .that(chunkRemoved.size()).isAtMost(maxRemovedPerChunk);
+        }
+        expect.withMessage("Removed identifier items")
+                .that(actualSet).containsExactlyElementsIn(expectedIdentifiers);
+    }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index d7723ac..464ecb2 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -37,7 +37,9 @@
 import android.hardware.broadcastradio.IBroadcastRadio;
 import android.hardware.broadcastradio.ITunerCallback;
 import android.hardware.broadcastradio.IdentifierType;
+import android.hardware.broadcastradio.ProgramFilter;
 import android.hardware.broadcastradio.ProgramInfo;
+import android.hardware.broadcastradio.ProgramListChunk;
 import android.hardware.broadcastradio.Result;
 import android.hardware.broadcastradio.VendorKeyValue;
 import android.hardware.radio.ProgramList;
@@ -61,8 +63,10 @@
 import org.mockito.Mock;
 import org.mockito.verification.VerificationWithTimeout;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Tests for AIDL HAL TunerSession.
@@ -72,7 +76,7 @@
     private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
     private static final VerificationWithTimeout CALLBACK_TIMEOUT =
             timeout(/* millis= */ 200);
-    private static final int SIGNAL_QUALITY = 1;
+    private static final int SIGNAL_QUALITY = 90;
     private static final long AM_FM_FREQUENCY_SPACING = 500;
     private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
     private static final RadioManager.FmBandDescriptor FM_BAND_DESCRIPTOR =
@@ -84,6 +88,27 @@
             new RadioManager.FmBandConfig(FM_BAND_DESCRIPTOR);
     private static final int UNSUPPORTED_CONFIG_FLAG = 0;
 
+    private static final ProgramSelector.Identifier TEST_FM_FREQUENCY_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
+                    /* value= */ 88_500);
+    private static final ProgramSelector.Identifier TEST_RDS_PI_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI,
+                    /* value= */ 15_019);
+
+    private static final RadioManager.ProgramInfo TEST_FM_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
+                    TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID, TEST_FM_FREQUENCY_ID,
+            SIGNAL_QUALITY);
+    private static final RadioManager.ProgramInfo TEST_FM_INFO_MODIFIED =
+            AidlTestUtils.makeProgramInfo(AidlTestUtils.makeProgramSelector(
+                    ProgramSelector.PROGRAM_TYPE_FM, TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID,
+                    TEST_FM_FREQUENCY_ID, /* signalQuality= */ 100);
+    private static final RadioManager.ProgramInfo TEST_RDS_INFO = AidlTestUtils.makeProgramInfo(
+            AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, TEST_RDS_PI_ID),
+            TEST_RDS_PI_ID, new ProgramSelector.Identifier(
+                    ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 89_500),
+            SIGNAL_QUALITY);
+
     // Mocks
     @Mock private IBroadcastRadio mBroadcastRadioMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
@@ -393,7 +418,7 @@
     }
 
     @Test
-    public void tune_withHalHasUnknownError_fails() throws Exception {
+    public void tune_withUnknownErrorFromHal_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
         doThrow(new ServiceSpecificException(Result.UNKNOWN_ERROR))
@@ -403,7 +428,7 @@
             mTunerSessions[0].tune(sel);
         });
 
-        assertWithMessage("Exception for tuning when HAL has unknown error")
+        assertWithMessage("Unknown error HAL exception when tuning")
                 .that(thrown).hasMessageThat().contains("UNKNOWN_ERROR");
     }
 
@@ -536,7 +561,7 @@
     }
 
     @Test
-    public void seek_withHalHasInternalError_fails() throws Exception {
+    public void seek_withInternalErrorFromHal_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         doThrow(new ServiceSpecificException(Result.INTERNAL_ERROR))
                 .when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean());
@@ -545,7 +570,7 @@
             mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
         });
 
-        assertWithMessage("Exception for seeking when HAL has internal error")
+        assertWithMessage("Internal error HAL exception when seeking")
                 .that(thrown).hasMessageThat().contains("INTERNAL_ERROR");
     }
 
@@ -644,11 +669,276 @@
     }
 
     @Test
+    public void startProgramListUpdates_withEmptyFilter() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter);
+        List<RadioManager.ProgramInfo> modified = List.of(TEST_FM_INFO, TEST_RDS_INFO);
+        List<ProgramSelector.Identifier> removed = new ArrayList<>();
+        ProgramListChunk halProgramList = AidlTestUtils.makeHalChunk(/* purge= */ true,
+                /* complete= */ true, modified, removed);
+        ProgramList.Chunk expectedProgramList =
+                AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, modified, removed);
+
+        mTunerSessions[0].startProgramListUpdates(filter);
+        mHalTunerCallback.onProgramListUpdated(halProgramList);
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+                .onProgramListUpdated(expectedProgramList);
+    }
+
+    @Test
+    public void startProgramListUpdates_withCallbackCalledForMultipleTimes() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        mTunerSessions[0].startProgramListUpdates(filter);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true,
+                /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true,
+                        List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+    }
+
+    @Test
+    public void startProgramListUpdates_withTheSameFilterForMultipleTimes() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        mTunerSessions[0].startProgramListUpdates(filter);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true,
+                /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true,
+                        List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+
+        mTunerSessions[0].startProgramListUpdates(filter);
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(any());
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true,
+                        List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>()));
+    }
+
+    @Test
+    public void startProgramListUpdates_withNullFilter() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+
+        mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true,
+                /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(any());
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true,
+                        List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID)));
+    }
+
+    @Test
+    public void startProgramListUpdates_withIdFilter() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter idFilter = new ProgramList.Filter(new ArraySet<>(),
+                Set.of(TEST_RDS_PI_ID), /* includeCategories= */ true,
+                /* excludeModifications= */ true);
+        ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(idFilter);
+
+        mTunerSessions[0].startProgramListUpdates(idFilter);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_RDS_INFO), new ArrayList<>()));
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_RDS_INFO), new ArrayList<>()));
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(any());
+    }
+
+    @Test
+    public void startProgramListUpdates_withFilterExcludingModifications() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filterExcludingModifications = new ProgramList.Filter(
+                Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ true);
+        ProgramFilter halFilter =
+                ConversionUtils.filterToHalProgramFilter(filterExcludingModifications);
+
+        mTunerSessions[0].startProgramListUpdates(filterExcludingModifications);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>()));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(any());
+    }
+
+    @Test
+    public void startProgramListUpdates_withFilterIncludingModifications() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filterIncludingModifications = new ProgramList.Filter(
+                Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        ProgramFilter halFilter =
+                ConversionUtils.filterToHalProgramFilter(filterIncludingModifications);
+
+        mTunerSessions[0].startProgramListUpdates(filterIncludingModifications);
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>()));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(
+                AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true,
+                        List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>()));
+    }
+
+    @Test
+    public void onProgramListUpdated_afterSessionClosed_doesNotUpdates() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        mTunerSessions[0].startProgramListUpdates(filter);
+
+        mTunerSessions[0].close();
+
+        verify(mBroadcastRadioMock).stopProgramListUpdates();
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any());
+    }
+
+    @Test
+    public void startProgramListUpdates_forMultipleSessions() throws Exception {
+        int numSessions = 3;
+        openAidlClients(numSessions);
+        ProgramList.Filter fmIdFilter = new ProgramList.Filter(new ArraySet<>(),
+                Set.of(TEST_FM_FREQUENCY_ID), /* includeCategories= */ false,
+                /* excludeModifications= */ true);
+        ProgramList.Filter filterExcludingCategories = new ProgramList.Filter(new ArraySet<>(),
+                new ArraySet<>(), /* includeCategories= */ true,
+                /* excludeModifications= */ true);
+        ProgramList.Filter rdsTypeFilter = new ProgramList.Filter(
+                Set.of(ProgramSelector.IDENTIFIER_TYPE_RDS_PI), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+
+        mTunerSessions[0].startProgramListUpdates(fmIdFilter);
+
+        ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(fmIdFilter);
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+
+        mTunerSessions[1].startProgramListUpdates(filterExcludingCategories);
+
+        halFilter.identifiers = new android.hardware.broadcastradio.ProgramIdentifier[]{};
+        halFilter.includeCategories = true;
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+
+        mTunerSessions[2].startProgramListUpdates(rdsTypeFilter);
+
+        halFilter.excludeModifications = false;
+        verify(mBroadcastRadioMock).startProgramListUpdates(halFilter);
+    }
+
+    @Test
+    public void onProgramListUpdated_forMultipleSessions() throws Exception {
+        int numSessions = 3;
+        openAidlClients(numSessions);
+        List<ProgramList.Filter> filters = List.of(new ProgramList.Filter(
+                        Set.of(ProgramSelector.IDENTIFIER_TYPE_RDS_PI), new ArraySet<>(),
+                        /* includeCategories= */ true, /* excludeModifications= */ false),
+                new ProgramList.Filter(new ArraySet<>(), Set.of(TEST_FM_FREQUENCY_ID),
+                        /* includeCategories= */ false, /* excludeModifications= */ true),
+                new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                        /* includeCategories= */ true, /* excludeModifications= */ true));
+
+        for (int index = 0; index < numSessions; index++) {
+            mTunerSessions[index].startProgramListUpdates(filters.get(index));
+        }
+
+        mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>()));
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
+                .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false,
+                        /* complete= */ true, List.of(TEST_RDS_INFO), new ArrayList<>()));
+        verify(mAidlTunerCallbackMocks[1], CALLBACK_TIMEOUT)
+                .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false,
+                        /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
+        verify(mAidlTunerCallbackMocks[2], CALLBACK_TIMEOUT)
+                .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false,
+                        /* complete= */ true, List.of(TEST_RDS_INFO, TEST_FM_INFO),
+                        new ArrayList<>()));
+    }
+
+    @Test
+    public void startProgramListUpdates_forNonCurrentUser_doesNotStartUpdates() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+
+        mTunerSessions[0].startProgramListUpdates(filter);
+
+        verify(mBroadcastRadioMock, never()).startProgramListUpdates(any());
+    }
+
+    @Test
+    public void startProgramListUpdates_withUnknownErrorFromHal_fails() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        doThrow(new ServiceSpecificException(Result.UNKNOWN_ERROR))
+                .when(mBroadcastRadioMock).startProgramListUpdates(any());
+
+        ParcelableException thrown = assertThrows(ParcelableException.class, () -> {
+            mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
+        });
+
+        assertWithMessage("Unknown error HAL exception when updating program list")
+                .that(thrown).hasMessageThat().contains("UNKNOWN_ERROR");
+    }
+
+    @Test
     public void stopProgramListUpdates() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
-        mTunerSessions[0].startProgramListUpdates(aidlFilter);
+        mTunerSessions[0].startProgramListUpdates(filter);
 
         mTunerSessions[0].stopProgramListUpdates();
 
@@ -658,9 +948,9 @@
     @Test
     public void stopProgramListUpdates_forNonCurrentUser_doesNotStopUpdates() throws Exception {
         openAidlClients(/* numClients= */ 1);
-        ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
                 /* includeCategories= */ true, /* excludeModifications= */ false);
-        mTunerSessions[0].startProgramListUpdates(aidlFilter);
+        mTunerSessions[0].startProgramListUpdates(filter);
         doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
 
         mTunerSessions[0].stopProgramListUpdates();
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index ea9a846..3815008 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -373,7 +373,7 @@
     }
 
     @Test
-    public void tune_withHalHasUnknownError_fails() throws Exception {
+    public void tune_withUnknownErrorFromHal_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
         doAnswer(invocation -> Result.UNKNOWN_ERROR).when(mHalTunerSessionMock).tune(any());
@@ -382,7 +382,7 @@
             mTunerSessions[0].tune(sel);
         });
 
-        assertWithMessage("Exception for tuning when HAL has unknown error")
+        assertWithMessage("Unknown error HAL exception when tuning")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
     }
 
@@ -513,7 +513,7 @@
     }
 
     @Test
-    public void seek_withHalHasInternalError_fails() throws Exception {
+    public void seek_withInternalErrorFromHal_fails() throws Exception {
         openAidlClients(/* numClients= */ 1);
         doAnswer(invocation -> Result.INTERNAL_ERROR).when(mHalTunerSessionMock)
                 .scan(anyBoolean(), anyBoolean());
@@ -522,7 +522,7 @@
             mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
         });
 
-        assertWithMessage("Exception for seeking when HAL has internal error")
+        assertWithMessage("Internal error HAL exception when seeking")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR));
     }
 
@@ -633,6 +633,32 @@
     }
 
     @Test
+    public void startProgramListUpdates_forNonCurrentUser_doesNotStartUpdates() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+                /* includeCategories= */ true, /* excludeModifications= */ false);
+        doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
+
+        mTunerSessions[0].startProgramListUpdates(filter);
+
+        verify(mHalTunerSessionMock, never()).startProgramListUpdates(any());
+    }
+
+    @Test
+    public void startProgramListUpdates_withUnknownErrorFromHal_fails() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        doAnswer(invocation -> Result.UNKNOWN_ERROR).when(mHalTunerSessionMock)
+                .startProgramListUpdates(any());
+
+        ParcelableException thrown = assertThrows(ParcelableException.class, () -> {
+            mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
+        });
+
+        assertWithMessage("Unknown error HAL exception when updating program list")
+                .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
+    }
+
+    @Test
     public void stopProgramListUpdates() throws Exception {
         openAidlClients(/* numClients= */ 1);
         ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index 625c318..249e246 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -16,6 +16,8 @@
 
 package android.content.res
 
+
+import android.platform.test.annotations.Presubmit
 import androidx.core.util.forEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -27,6 +29,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@Presubmit
 @RunWith(AndroidJUnit4::class)
 class FontScaleConverterFactoryTest {
 
@@ -79,10 +82,10 @@
     @LargeTest
     @Test
     fun allFeasibleScalesAndConversionsDoNotCrash() {
-        generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+        generateSequenceOfFractions(-10f..10f, step = 0.01f)
             .mapNotNull{ FontScaleConverterFactory.forScale(it) }
             .flatMap{ table ->
-                generateSequenceOfFractions(-10000f..10000f, step = 0.01f)
+                generateSequenceOfFractions(-2000f..2000f, step = 0.01f)
                     .map{ Pair(table, it) }
             }
             .forEach { (table, sp) ->
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
index e9f850e..bfa8c9a 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
@@ -16,11 +16,13 @@
 
 package android.content.res
 
+import android.platform.test.annotations.Presubmit
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@Presubmit
 @RunWith(AndroidJUnit4::class)
 class FontScaleConverterTest {
 
diff --git a/core/tests/coretests/src/android/util/TypedValueTest.kt b/core/tests/coretests/src/android/util/TypedValueTest.kt
index b020c38..af01447 100644
--- a/core/tests/coretests/src/android/util/TypedValueTest.kt
+++ b/core/tests/coretests/src/android/util/TypedValueTest.kt
@@ -17,6 +17,7 @@
 package android.util
 
 import android.content.res.FontScaleConverterFactory
+import android.platform.test.annotations.Presubmit
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SmallTest
@@ -30,6 +31,7 @@
 import kotlin.math.min
 import kotlin.math.roundToInt
 
+@Presubmit
 @RunWith(AndroidJUnit4::class)
 class TypedValueTest {
     @LargeTest
@@ -223,7 +225,6 @@
         metrics.scaledDensity = 0f
 
         listOf(
-            TypedValue.COMPLEX_UNIT_PX,
             TypedValue.COMPLEX_UNIT_DIP,
             TypedValue.COMPLEX_UNIT_SP,
             TypedValue.COMPLEX_UNIT_PT,
@@ -257,8 +258,7 @@
             TypedValue.COMPLEX_UNIT_MM
         )
             .forEach { dimenType ->
-                // Test for every integer value in the range...
-                for (i: Int in -(1 shl 23) until (1 shl 23)) {
+                for (i: Int in -10000 until 10000) {
                     assertRoundTripIsEqual(i.toFloat(), dimenType, metrics)
                     assertRoundTripIsEqual(i - .1f, dimenType, metrics)
                     assertRoundTripIsEqual(i + .5f, dimenType, metrics)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index af31391..6230d22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -17,6 +17,7 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -684,7 +685,8 @@
         if (bubble.getPendingIntentCanceled()
                 || !(reason == Bubbles.DISMISS_AGED
                 || reason == Bubbles.DISMISS_USER_GESTURE
-                || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
+                || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)
+                || KEY_APP_BUBBLE.equals(bubble.getKey())) {
             return;
         }
         if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8ddc3c04..1488469 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -605,9 +605,19 @@
             float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (taskId2 == INVALID_TASK_ID) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.startTask(taskId1, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.startTask(taskId1, options1);
-
         startWithLegacyTransition(wct, taskId2, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
@@ -632,9 +642,19 @@
             InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         if (options1 == null) options1 = new Bundle();
+        if (taskId == INVALID_TASK_ID) {
+            // Launching a solo task.
+            ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+            activityOptions.update(ActivityOptions.makeRemoteAnimation(adapter));
+            options1 = activityOptions.toBundle();
+            addActivityOptions(options1, null /* launchTarget */);
+            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+            mSyncQueue.queue(wct);
+            return;
+        }
+
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
-
         startWithLegacyTransition(wct, taskId, options2, splitPosition, splitRatio, adapter,
                 instanceId);
     }
@@ -696,6 +716,34 @@
         mShouldUpdateRecents = false;
         mIsSplitEntering = true;
 
+        setSideStagePosition(sidePosition, wct);
+        if (!mMainStage.isActive()) {
+            mMainStage.activate(wct, false /* reparent */);
+        }
+
+        if (mainOptions == null) mainOptions = new Bundle();
+        addActivityOptions(mainOptions, mMainStage);
+        mainOptions = wrapAsSplitRemoteAnimation(adapter, mainOptions);
+
+        updateWindowBounds(mSplitLayout, wct);
+        if (mainTaskId == INVALID_TASK_ID) {
+            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, mainOptions);
+        } else {
+            wct.startTask(mainTaskId, mainOptions);
+        }
+
+        wct.reorder(mRootTaskInfo.token, true);
+        wct.setForceTranslucent(mRootTaskInfo.token, false);
+
+        mSyncQueue.queue(wct);
+        mSyncQueue.runInSync(t -> {
+            setDividerVisibility(true, t);
+        });
+
+        setEnterInstanceId(instanceId);
+    }
+
+    private Bundle wrapAsSplitRemoteAnimation(RemoteAnimationAdapter adapter, Bundle options) {
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         if (isSplitScreenVisible()) {
             mMainStage.evictAllChildren(evictWct);
@@ -739,37 +787,9 @@
         };
         RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
                 wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
-
-        if (mainOptions == null) {
-            mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
-        } else {
-            ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
-            mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-            mainOptions = mainActivityOptions.toBundle();
-        }
-
-        setSideStagePosition(sidePosition, wct);
-        if (!mMainStage.isActive()) {
-            mMainStage.activate(wct, false /* reparent */);
-        }
-
-        if (mainOptions == null) mainOptions = new Bundle();
-        addActivityOptions(mainOptions, mMainStage);
-        updateWindowBounds(mSplitLayout, wct);
-        if (mainTaskId == INVALID_TASK_ID) {
-            wct.sendPendingIntent(mainPendingIntent, mainFillInIntent, mainOptions);
-        } else {
-            wct.startTask(mainTaskId, mainOptions);
-        }
-        wct.reorder(mRootTaskInfo.token, true);
-        wct.setForceTranslucent(mRootTaskInfo.token, false);
-
-        mSyncQueue.queue(wct);
-        mSyncQueue.runInSync(t -> {
-            setDividerVisibility(true, t);
-        });
-
-        setEnterInstanceId(instanceId);
+        ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+        activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+        return activityOptions.toBundle();
     }
 
     private void setEnterInstanceId(InstanceId instanceId) {
@@ -1228,8 +1248,10 @@
         return SPLIT_POSITION_UNDEFINED;
     }
 
-    private void addActivityOptions(Bundle opts, StageTaskListener stage) {
-        opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
+    private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
+        if (launchTarget != null) {
+            opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, launchTarget.mRootTaskInfo.token);
+        }
         // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
         // will be canceled.
         opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index e6711ac..8b025cd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.bubbles;
 
+import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -32,6 +34,7 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.content.Intent;
 import android.content.LocusId;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -94,6 +97,7 @@
     private Bubble mBubbleInterruptive;
     private Bubble mBubbleDismissed;
     private Bubble mBubbleLocusId;
+    private Bubble mAppBubble;
 
     private BubbleData mBubbleData;
     private TestableBubblePositioner mPositioner;
@@ -178,6 +182,11 @@
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
                 mMainExecutor);
+
+        Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
+        appBubbleIntent.setPackage(mContext.getPackageName());
+        mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mMainExecutor);
+
         mPositioner = new TestableBubblePositioner(mContext,
                 mock(WindowManager.class));
         mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner,
@@ -1089,6 +1098,18 @@
         assertOverflowChangedTo(ImmutableList.of());
     }
 
+    @Test
+    public void test_removeAppBubble_skipsOverflow() {
+        mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
+                false /* showInShade */);
+        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isEqualTo(mAppBubble);
+
+        mBubbleData.dismissBubbleWithKey(KEY_APP_BUBBLE, Bubbles.DISMISS_USER_GESTURE);
+
+        assertThat(mBubbleData.getOverflowBubbleWithKey(KEY_APP_BUBBLE)).isNull();
+        assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
+    }
+
     private void verifyUpdateReceived() {
         verify(mListener).applyUpdate(mUpdateCaptor.capture());
         reset(mListener);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 23cf913..aeead5e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -215,6 +215,15 @@
     path: "apex/java",
 }
 
+java_api_contribution {
+    name: "framework-graphics-public-stubs",
+    api_surface: "public",
+    api_file: "api/current.txt",
+    visibility: [
+        "//build/orchestrator/apis",
+    ],
+}
+
 // ------------------------
 // APEX
 // ------------------------
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 2539694..b7d4dc9 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -16,11 +16,13 @@
 
 #pragma once
 
-#include "CanvasTransform.h"
-#include "hwui/Bitmap.h"
-#include "utils/Macros.h"
-#include "utils/TypeLogic.h"
+#include <SkRuntimeEffect.h>
+#include <log/log.h>
 
+#include <cstdlib>
+#include <vector>
+
+#include "CanvasTransform.h"
 #include "SkCanvas.h"
 #include "SkCanvasVirtualEnforcer.h"
 #include "SkDrawable.h"
@@ -28,11 +30,11 @@
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkRect.h"
-
+#include "hwui/Bitmap.h"
 #include "pipeline/skia/AnimatedDrawables.h"
-
-#include <SkRuntimeEffect.h>
-#include <vector>
+#include "utils/AutoMalloc.h"
+#include "utils/Macros.h"
+#include "utils/TypeLogic.h"
 
 enum class SkBlendMode;
 class SkRRect;
@@ -145,7 +147,7 @@
     template <typename Fn, typename... Args>
     void map(const Fn[], Args...) const;
 
-    SkAutoTMalloc<uint8_t> fBytes;
+    AutoTMalloc<uint8_t> fBytes;
     size_t fUsed = 0;
     size_t fReserved = 0;
 
diff --git a/libs/hwui/utils/AutoMalloc.h b/libs/hwui/utils/AutoMalloc.h
new file mode 100644
index 0000000..05f5e9f
--- /dev/null
+++ b/libs/hwui/utils/AutoMalloc.h
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdlib>
+#include <memory>
+#include <type_traits>
+
+namespace android {
+namespace uirenderer {
+
+/** Manages an array of T elements, freeing the array in the destructor.
+ *  Does NOT call any constructors/destructors on T (T must be POD).
+ */
+template <typename T,
+          typename = std::enable_if_t<std::is_trivially_default_constructible<T>::value &&
+                                      std::is_trivially_destructible<T>::value>>
+class AutoTMalloc {
+public:
+    /** Takes ownership of the ptr. The ptr must be a value which can be passed to std::free. */
+    explicit AutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {}
+
+    /** Allocates space for 'count' Ts. */
+    explicit AutoTMalloc(size_t count) : fPtr(mallocIfCountThrowOnFail(count)) {}
+
+    AutoTMalloc(AutoTMalloc&&) = default;
+    AutoTMalloc& operator=(AutoTMalloc&&) = default;
+
+    /** Resize the memory area pointed to by the current ptr preserving contents. */
+    void realloc(size_t count) { fPtr.reset(reallocIfCountThrowOnFail(count)); }
+
+    /** Resize the memory area pointed to by the current ptr without preserving contents. */
+    T* reset(size_t count = 0) {
+        fPtr.reset(mallocIfCountThrowOnFail(count));
+        return this->get();
+    }
+
+    T* get() const { return fPtr.get(); }
+
+    operator T*() { return fPtr.get(); }
+
+    operator const T*() const { return fPtr.get(); }
+
+    T& operator[](int index) { return fPtr.get()[index]; }
+
+    const T& operator[](int index) const { return fPtr.get()[index]; }
+
+    /**
+     *  Transfer ownership of the ptr to the caller, setting the internal
+     *  pointer to NULL. Note that this differs from get(), which also returns
+     *  the pointer, but it does not transfer ownership.
+     */
+    T* release() { return fPtr.release(); }
+
+private:
+    struct FreeDeleter {
+        void operator()(uint8_t* p) { std::free(p); }
+    };
+    std::unique_ptr<T, FreeDeleter> fPtr;
+
+    T* mallocIfCountThrowOnFail(size_t count) {
+        T* newPtr = nullptr;
+        if (count) {
+            newPtr = (T*)std::malloc(count * sizeof(T));
+            LOG_ALWAYS_FATAL_IF(!newPtr, "failed to malloc %zu bytes", count * sizeof(T));
+        }
+        return newPtr;
+    }
+    T* reallocIfCountThrowOnFail(size_t count) {
+        T* newPtr = nullptr;
+        if (count) {
+            newPtr = (T*)std::realloc(fPtr.release(), count * sizeof(T));
+            LOG_ALWAYS_FATAL_IF(!newPtr, "failed to realloc %zu bytes", count * sizeof(T));
+        }
+        return newPtr;
+    }
+};
+
+}  // namespace uirenderer
+}  // namespace android
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4475aed..24c5b41 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7022,6 +7022,10 @@
      * Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
      * currently connected to the system and meeting the criteria specified in the
      * <code>flags</code> parameter.
+     * Notes that Android audio framework only support one device per device type. In that case,
+     * if there are multiple audio device with the same device type connected to the Android device,
+     * only the last reported device will be known by Android audio framework and returned by this
+     * API.
      * @param flags A set of bitflags specifying the criteria to test.
      * @see #GET_DEVICES_OUTPUTS
      * @see #GET_DEVICES_INPUTS
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 90874bb..06c3476 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -123,7 +123,7 @@
         Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW,
         Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
         Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
-        Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
+        Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
         Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
         Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 62f4c41..d72d4d5 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -178,7 +178,7 @@
         VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
                 NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
-        VALIDATORS.put(Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.SFPS_PERFORMANT_AUTH_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4be1d30..ecb88f6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -415,7 +415,6 @@
 
         <service android:name=".screenshot.ScreenshotCrossProfileService"
                  android:permission="com.android.systemui.permission.SELF"
-                 android:process=":screenshot_cross_profile"
                  android:exported="false" />
 
         <service android:name=".screenrecord.RecordingService"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index 2903288..3d341af 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -33,7 +33,9 @@
 private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
 private const val FONT_ITALIC_DEFAULT_VALUE = 0f
 
-/** Provide interpolation of two fonts by adjusting font variation settings. */
+/**
+ * Provide interpolation of two fonts by adjusting font variation settings.
+ */
 class FontInterpolator {
 
     /**
@@ -59,14 +61,11 @@
         var index: Int,
         val sortedAxes: MutableList<FontVariationAxis>
     ) {
-        constructor(
-            font: Font,
-            axes: List<FontVariationAxis>
-        ) : this(
-            font.sourceIdentifier,
-            font.ttcIndex,
-            axes.toMutableList().apply { sortBy { it.tag } }
-        )
+        constructor(font: Font, axes: List<FontVariationAxis>) :
+                this(font.sourceIdentifier,
+                        font.ttcIndex,
+                        axes.toMutableList().apply { sortBy { it.tag } }
+                )
 
         fun set(font: Font, axes: List<FontVariationAxis>) {
             sourceId = font.sourceIdentifier
@@ -87,7 +86,9 @@
     private val tmpInterpKey = InterpKey(null, null, 0f)
     private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
 
-    /** Linear interpolate the font variation settings. */
+    /**
+     * Linear interpolate the font variation settings.
+     */
     fun lerp(start: Font, end: Font, progress: Float): Font {
         if (progress == 0f) {
             return start
@@ -114,34 +115,27 @@
         // this doesn't take much time since the variation axes is usually up to 5. If we need to
         // support more number of axes, we may want to preprocess the font and store the sorted axes
         // and also pre-fill the missing axes value with default value from 'fvar' table.
-        val newAxes =
-            lerp(startAxes, endAxes) { tag, startValue, endValue ->
-                when (tag) {
-                    // TODO: Good to parse 'fvar' table for retrieving default value.
-                    TAG_WGHT ->
-                        adjustWeight(
-                            MathUtils.lerp(
+        val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue ->
+            when (tag) {
+                // TODO: Good to parse 'fvar' table for retrieving default value.
+                TAG_WGHT -> adjustWeight(
+                        MathUtils.lerp(
                                 startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
                                 endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
-                                progress
-                            )
-                        )
-                    TAG_ITAL ->
-                        adjustItalic(
-                            MathUtils.lerp(
+                                progress))
+                TAG_ITAL -> adjustItalic(
+                        MathUtils.lerp(
                                 startValue ?: FONT_ITALIC_DEFAULT_VALUE,
                                 endValue ?: FONT_ITALIC_DEFAULT_VALUE,
-                                progress
-                            )
-                        )
-                    else -> {
-                        require(startValue != null && endValue != null) {
-                            "Unable to interpolate due to unknown default axes value : $tag"
-                        }
-                        MathUtils.lerp(startValue, endValue, progress)
+                                progress))
+                else -> {
+                    require(startValue != null && endValue != null) {
+                        "Unable to interpolate due to unknown default axes value : $tag"
                     }
+                    MathUtils.lerp(startValue, endValue, progress)
                 }
             }
+        }
 
         // Check if we already make font for this axes. This is typically happens if the animation
         // happens backward.
@@ -155,7 +149,9 @@
         // This is the first time to make the font for the axes. Build and store it to the cache.
         // Font.Builder#build won't throw IOException since creating fonts from existing fonts will
         // not do any IO work.
-        val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
+        val newFont = Font.Builder(start)
+                .setFontVariationSettings(newAxes.toTypedArray())
+                .build()
         interpCache[InterpKey(start, end, progress)] = newFont
         verFontCache[VarFontKey(start, newAxes)] = newFont
         return newFont
@@ -177,28 +173,26 @@
             val tagA = if (i < start.size) start[i].tag else null
             val tagB = if (j < end.size) end[j].tag else null
 
-            val comp =
-                when {
-                    tagA == null -> 1
-                    tagB == null -> -1
-                    else -> tagA.compareTo(tagB)
-                }
+            val comp = when {
+                tagA == null -> 1
+                tagB == null -> -1
+                else -> tagA.compareTo(tagB)
+            }
 
-            val axis =
-                when {
-                    comp == 0 -> {
-                        val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
-                        FontVariationAxis(tagA, v)
-                    }
-                    comp < 0 -> {
-                        val v = filter(tagA!!, start[i++].styleValue, null)
-                        FontVariationAxis(tagA, v)
-                    }
-                    else -> { // comp > 0
-                        val v = filter(tagB!!, null, end[j++].styleValue)
-                        FontVariationAxis(tagB, v)
-                    }
+            val axis = when {
+                comp == 0 -> {
+                    val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
+                    FontVariationAxis(tagA, v)
                 }
+                comp < 0 -> {
+                    val v = filter(tagA!!, start[i++].styleValue, null)
+                    FontVariationAxis(tagA, v)
+                }
+                else -> { // comp > 0
+                    val v = filter(tagB!!, null, end[j++].styleValue)
+                    FontVariationAxis(tagB, v)
+                }
+            }
 
             result.add(axis)
         }
@@ -208,21 +202,21 @@
     // For the performance reasons, we animate weight with FONT_WEIGHT_ANIMATION_STEP. This helps
     // Cache hit ratio in the Skia glyph cache.
     private fun adjustWeight(value: Float) =
-        coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
+            coerceInWithStep(value, FONT_WEIGHT_MIN, FONT_WEIGHT_MAX, FONT_WEIGHT_ANIMATION_STEP)
 
     // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
     // Cache hit ratio in the Skia glyph cache.
     private fun adjustItalic(value: Float) =
-        coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
+            coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
 
     private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
-        (v.coerceIn(min, max) / step).toInt() * step
+            (v.coerceIn(min, max) / step).toInt() * step
 
     companion object {
         private val EMPTY_AXES = arrayOf<FontVariationAxis>()
 
         // Returns true if given two font instance can be interpolated.
         fun canInterpolate(start: Font, end: Font) =
-            start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
+                start.ttcIndex == end.ttcIndex && start.sourceIdentifier == end.sourceIdentifier
     }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 65d6c83..5f1bb83 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -36,7 +36,8 @@
  * Currently this class can provide text style animation for text weight and text size. For example
  * the simple view that draws text with animating text size is like as follows:
  *
- * ```
+ * <pre>
+ * <code>
  *     class SimpleTextAnimation : View {
  *         @JvmOverloads constructor(...)
  *
@@ -52,34 +53,39 @@
  *             animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
  *         }
  *     }
- * ```
+ * </code>
+ * </pre>
  */
-class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
+class TextAnimator(
+    layout: Layout,
+    private val invalidateCallback: () -> Unit
+) {
     // Following two members are for mutable for testing purposes.
     public var textInterpolator: TextInterpolator = TextInterpolator(layout)
-    public var animator: ValueAnimator =
-        ValueAnimator.ofFloat(1f).apply {
-            duration = DEFAULT_ANIMATION_DURATION
-            addUpdateListener {
-                textInterpolator.progress = it.animatedValue as Float
-                invalidateCallback()
-            }
-            addListener(
-                object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator?) {
-                        textInterpolator.rebase()
-                    }
-                    override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
-                }
-            )
+    public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply {
+        duration = DEFAULT_ANIMATION_DURATION
+        addUpdateListener {
+            textInterpolator.progress = it.animatedValue as Float
+            invalidateCallback()
         }
+        addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator?) {
+                textInterpolator.rebase()
+            }
+            override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
+        })
+    }
 
     sealed class PositionedGlyph {
 
-        /** Mutable X coordinate of the glyph position relative from drawing offset. */
+        /**
+         * Mutable X coordinate of the glyph position relative from drawing offset.
+         */
         var x: Float = 0f
 
-        /** Mutable Y coordinate of the glyph position relative from the baseline. */
+        /**
+         * Mutable Y coordinate of the glyph position relative from the baseline.
+         */
         var y: Float = 0f
 
         /**
@@ -90,29 +96,40 @@
         /**
          * Mutable text size of the glyph in pixels.
          */
-        /** Mutable text size of the glyph in pixels. */
         var textSize: Float = 0f
 
-        /** Mutable color of the glyph. */
+        /**
+         * Mutable color of the glyph.
+         */
         var color: Int = 0
 
-        /** Immutable character offset in the text that the current font run start. */
+        /**
+         * Immutable character offset in the text that the current font run start.
+         */
         abstract var runStart: Int
             protected set
 
-        /** Immutable run length of the font run. */
+        /**
+         * Immutable run length of the font run.
+         */
         abstract var runLength: Int
             protected set
 
-        /** Immutable glyph index of the font run. */
+        /**
+         * Immutable glyph index of the font run.
+         */
         abstract var glyphIndex: Int
             protected set
 
-        /** Immutable font instance for this font run. */
+        /**
+         * Immutable font instance for this font run.
+         */
         abstract var font: Font
             protected set
 
-        /** Immutable glyph ID for this glyph. */
+        /**
+         * Immutable glyph ID for this glyph.
+         */
         abstract var glyphId: Int
             protected set
     }
@@ -130,30 +147,30 @@
     /**
      * GlyphFilter applied just before drawing to canvas for tweaking positions and text size.
      *
-     * This callback is called for each glyphs just before drawing the glyphs. This function will be
-     * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate
-     * the position, size and color for tweaking animations. Do not keep the reference of passed
-     * glyph object. The interpolator reuses that object for avoiding object allocations.
+     * This callback is called for each glyphs just before drawing the glyphs. This function will
+     * be called with the intrinsic position, size, color, glyph ID and font instance. You can
+     * mutate the position, size and color for tweaking animations.
+     * Do not keep the reference of passed glyph object. The interpolator reuses that object for
+     * avoiding object allocations.
      *
-     * Details: The text is drawn with font run units. The font run is a text segment that draws
-     * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in
-     * the text that current glyph is in. Once the font run is determined, the system will convert
-     * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code
-     * glyphIndex} is the offset of the converted glyph array. Please note that the {@code
-     * glyphIndex} is not a character index, because the character will not be converted to glyph
-     * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
+     * Details:
+     * The text is drawn with font run units. The font run is a text segment that draws with the
+     * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text
+     * that current glyph is in. Once the font run is determined, the system will convert characters
+     * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and
+     * {@code glyphIndex} is the offset of the converted glyph array. Please note that the
+     * {@code glyphIndex} is not a character index, because the character will not be converted to
+     * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be
      * composed from multiple characters.
      *
      * Here is an example of font runs: "fin. 終わり"
      *
-     * ```
      * Characters :    f      i      n      .      _      終     わ     り
      * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A
      * Font Runs  : <-- Roboto-Regular.ttf          --><-- NotoSans-CJK.otf -->
      *                  runStart = 0, runLength = 5        runStart = 5, runLength = 3
      * Glyph IDs  :      194        48     7      8     4367   1039   1002
      * Glyph Index:       0          1     2      3       0      1      2
-     * ```
      *
      * In this example, the "fi" is converted into ligature form, thus the single glyph ID is
      * assigned for two characters, f and i.
@@ -176,29 +193,28 @@
      */
     var glyphFilter: GlyphCallback?
         get() = textInterpolator.glyphFilter
-        set(value) {
-            textInterpolator.glyphFilter = value
-        }
+        set(value) { textInterpolator.glyphFilter = value }
 
     fun draw(c: Canvas) = textInterpolator.draw(c)
 
     /**
      * Set text style with animation.
      *
-     * By passing -1 to weight, the view preserve the current weight. By passing -1 to textSize, the
-     * view preserve the current text size. Bu passing -1 to duration, the default text animation,
-     * 1000ms, is used. By passing false to animate, the text will be updated without animation.
+     * By passing -1 to weight, the view preserve the current weight.
+     * By passing -1 to textSize, the view preserve the current text size.
+     * Bu passing -1 to duration, the default text animation, 1000ms, is used.
+     * By passing false to animate, the text will be updated without animation.
      *
      * @param weight an optional text weight.
      * @param textSize an optional font size.
-     * @param colors an optional colors array that must be the same size as numLines passed to the
-     * TextInterpolator
+     * @param colors an optional colors array that must be the same size as numLines passed to
+     *  the TextInterpolator
      * @param animate an optional boolean indicating true for showing style transition as animation,
-     * false for immediate style transition. True by default.
+     *                false for immediate style transition. True by default.
      * @param duration an optional animation duration in milliseconds. This is ignored if animate is
-     * false.
+     *                 false.
      * @param interpolator an optional time interpolator. If null is passed, last set interpolator
-     * will be used. This is ignored if animate is false.
+     *                     will be used. This is ignored if animate is false.
      */
     fun setTextStyle(
         weight: Int = -1,
@@ -221,11 +237,10 @@
         if (weight >= 0) {
             // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
             // memory impact, cache the typeface result.
-            textInterpolator.targetPaint.typeface =
-                typefaceCache.getOrElse(weight) {
-                    textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
-                    textInterpolator.targetPaint.typeface
-                }
+            textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) {
+                textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+                textInterpolator.targetPaint.typeface
+            }
         }
         if (color != null) {
             textInterpolator.targetPaint.color = color
@@ -234,24 +249,22 @@
 
         if (animate) {
             animator.startDelay = delay
-            animator.duration =
-                if (duration == -1L) {
-                    DEFAULT_ANIMATION_DURATION
-                } else {
-                    duration
-                }
+            animator.duration = if (duration == -1L) {
+                DEFAULT_ANIMATION_DURATION
+            } else {
+                duration
+            }
             interpolator?.let { animator.interpolator = it }
             if (onAnimationEnd != null) {
-                val listener =
-                    object : AnimatorListenerAdapter() {
-                        override fun onAnimationEnd(animation: Animator?) {
-                            onAnimationEnd.run()
-                            animator.removeListener(this)
-                        }
-                        override fun onAnimationCancel(animation: Animator?) {
-                            animator.removeListener(this)
-                        }
+                val listener = object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator?) {
+                        onAnimationEnd.run()
+                        animator.removeListener(this)
                     }
+                    override fun onAnimationCancel(animation: Animator?) {
+                        animator.removeListener(this)
+                    }
+                }
                 animator.addListener(listener)
             }
             animator.start()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index f9fb42c..0448c81 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -26,8 +26,12 @@
 import com.android.internal.graphics.ColorUtils
 import java.lang.Math.max
 
-/** Provide text style linear interpolation for plain text. */
-class TextInterpolator(layout: Layout) {
+/**
+ * Provide text style linear interpolation for plain text.
+ */
+class TextInterpolator(
+    layout: Layout
+) {
 
     /**
      * Returns base paint used for interpolation.
@@ -60,11 +64,12 @@
         var baseFont: Font,
         var targetFont: Font
     ) {
-        val length: Int
-            get() = end - start
+        val length: Int get() = end - start
     }
 
-    /** A class represents text layout of a single run. */
+    /**
+     * A class represents text layout of a single run.
+     */
     private class Run(
         val glyphIds: IntArray,
         val baseX: FloatArray, // same length as glyphIds
@@ -74,8 +79,12 @@
         val fontRuns: List<FontRun>
     )
 
-    /** A class represents text layout of a single line. */
-    private class Line(val runs: List<Run>)
+    /**
+     * A class represents text layout of a single line.
+     */
+    private class Line(
+        val runs: List<Run>
+    )
 
     private var lines = listOf<Line>()
     private val fontInterpolator = FontInterpolator()
@@ -97,8 +106,8 @@
     /**
      * The layout used for drawing text.
      *
-     * Only non-styled text is supported. Even if the given layout is created from Spanned, the span
-     * information is not used.
+     * Only non-styled text is supported. Even if the given layout is created from Spanned, the
+     * span information is not used.
      *
      * The paint objects used for interpolation are not changed by this method call.
      *
@@ -121,8 +130,8 @@
     /**
      * Recalculate internal text layout for interpolation.
      *
-     * Whenever the target paint is modified, call this method to recalculate internal text layout
-     * used for interpolation.
+     * Whenever the target paint is modified, call this method to recalculate internal
+     * text layout used for interpolation.
      */
     fun onTargetPaintModified() {
         updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false)
@@ -131,8 +140,8 @@
     /**
      * Recalculate internal text layout for interpolation.
      *
-     * Whenever the base paint is modified, call this method to recalculate internal text layout
-     * used for interpolation.
+     * Whenever the base paint is modified, call this method to recalculate internal
+     * text layout used for interpolation.
      */
     fun onBasePaintModified() {
         updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true)
@@ -143,11 +152,11 @@
      *
      * The text interpolator does not calculate all the text position by text shaper due to
      * performance reasons. Instead, the text interpolator shape the start and end state and
-     * calculate text position of the middle state by linear interpolation. Due to this trick, the
-     * text positions of the middle state is likely different from the text shaper result. So, if
-     * you want to start animation from the middle state, you will see the glyph jumps due to this
-     * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from
-     * text shape result of weight 550.
+     * calculate text position of the middle state by linear interpolation. Due to this trick,
+     * the text positions of the middle state is likely different from the text shaper result.
+     * So, if you want to start animation from the middle state, you will see the glyph jumps due to
+     * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different
+     * from text shape result of weight 550.
      *
      * After calling this method, do not call onBasePaintModified() since it reshape the text and
      * update the base state. As in above notice, the text shaping result at current progress is
@@ -159,7 +168,8 @@
      * animate weight from 200 to 400, then if you want to move back to 200 at the half of the
      * animation, it will look like
      *
-     * ```
+     * <pre>
+     * <code>
      *     val interp = TextInterpolator(layout)
      *
      *     // Interpolate between weight 200 to 400.
@@ -189,7 +199,9 @@
      *         // progress is 0.5
      *         animator.start()
      *     }
-     * ```
+     * </code>
+     * </pre>
+     *
      */
     fun rebase() {
         if (progress == 0f) {
@@ -251,75 +263,69 @@
         }
 
         var maxRunLength = 0
-        lines =
-            baseLayout.zip(targetLayout) { baseLine, targetLine ->
-                val runs =
-                    baseLine.zip(targetLine) { base, target ->
-                        require(base.glyphCount() == target.glyphCount()) {
-                            "Inconsistent glyph count at line ${lines.size}"
+        lines = baseLayout.zip(targetLayout) { baseLine, targetLine ->
+            val runs = baseLine.zip(targetLine) { base, target ->
+
+                require(base.glyphCount() == target.glyphCount()) {
+                    "Inconsistent glyph count at line ${lines.size}"
+                }
+
+                val glyphCount = base.glyphCount()
+
+                // Good to recycle the array if the existing array can hold the new layout result.
+                val glyphIds = IntArray(glyphCount) {
+                    base.getGlyphId(it).also { baseGlyphId ->
+                        require(baseGlyphId == target.getGlyphId(it)) {
+                            "Inconsistent glyph ID at $it in line ${lines.size}"
                         }
+                    }
+                }
 
-                        val glyphCount = base.glyphCount()
+                val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+                val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+                val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+                val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
 
-                        // Good to recycle the array if the existing array can hold the new layout
-                        // result.
-                        val glyphIds =
-                            IntArray(glyphCount) {
-                                base.getGlyphId(it).also { baseGlyphId ->
-                                    require(baseGlyphId == target.getGlyphId(it)) {
-                                        "Inconsistent glyph ID at $it in line ${lines.size}"
-                                    }
-                                }
+                // Calculate font runs
+                val fontRun = mutableListOf<FontRun>()
+                if (glyphCount != 0) {
+                    var start = 0
+                    var baseFont = base.getFont(start)
+                    var targetFont = target.getFont(start)
+                    require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+                        "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+                    }
+
+                    for (i in 1 until glyphCount) {
+                        val nextBaseFont = base.getFont(i)
+                        val nextTargetFont = target.getFont(i)
+
+                        if (baseFont !== nextBaseFont) {
+                            require(targetFont !== nextTargetFont) {
+                                "Base font has changed at $i but target font has not changed."
                             }
-
-                        val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
-                        val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
-                        val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
-                        val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
-
-                        // Calculate font runs
-                        val fontRun = mutableListOf<FontRun>()
-                        if (glyphCount != 0) {
-                            var start = 0
-                            var baseFont = base.getFont(start)
-                            var targetFont = target.getFont(start)
+                            // Font transition point. push run and reset context.
+                            fontRun.add(FontRun(start, i, baseFont, targetFont))
+                            maxRunLength = max(maxRunLength, i - start)
+                            baseFont = nextBaseFont
+                            targetFont = nextTargetFont
+                            start = i
                             require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
                                 "Cannot interpolate font at $start ($baseFont vs $targetFont)"
                             }
-
-                            for (i in 1 until glyphCount) {
-                                val nextBaseFont = base.getFont(i)
-                                val nextTargetFont = target.getFont(i)
-
-                                if (baseFont !== nextBaseFont) {
-                                    require(targetFont !== nextTargetFont) {
-                                        "Base font has changed at $i but target font has not " +
-                                            "changed."
-                                    }
-                                    // Font transition point. push run and reset context.
-                                    fontRun.add(FontRun(start, i, baseFont, targetFont))
-                                    maxRunLength = max(maxRunLength, i - start)
-                                    baseFont = nextBaseFont
-                                    targetFont = nextTargetFont
-                                    start = i
-                                    require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
-                                        "Cannot interpolate font at $start ($baseFont vs " +
-                                            "$targetFont)"
-                                    }
-                                } else { // baseFont === nextBaseFont
-                                    require(targetFont === nextTargetFont) {
-                                        "Base font has not changed at $i but target font has " +
-                                            "changed."
-                                    }
-                                }
+                        } else { // baseFont === nextBaseFont
+                            require(targetFont === nextTargetFont) {
+                                "Base font has not changed at $i but target font has changed."
                             }
-                            fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
-                            maxRunLength = max(maxRunLength, glyphCount - start)
                         }
-                        Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
                     }
-                Line(runs)
+                    fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+                    maxRunLength = max(maxRunLength, glyphCount - start)
+                }
+                Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
             }
+            Line(runs)
+        }
 
         // Update float array used for drawing.
         if (tmpPositionArray.size < maxRunLength * 2) {
@@ -351,9 +357,9 @@
         if (glyphFilter == null) {
             for (i in run.start until run.end) {
                 tmpPositionArray[arrayIndex++] =
-                    MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
+                        MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
                 tmpPositionArray[arrayIndex++] =
-                    MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
+                        MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
             }
             c.drawGlyphs(line.glyphIds, run.start, tmpPositionArray, 0, run.length, font, paint)
             return
@@ -382,14 +388,13 @@
                 tmpPaintForGlyph.color = tmpGlyph.color
 
                 c.drawGlyphs(
-                    line.glyphIds,
-                    prevStart,
-                    tmpPositionArray,
-                    0,
-                    i - prevStart,
-                    font,
-                    tmpPaintForGlyph
-                )
+                        line.glyphIds,
+                        prevStart,
+                        tmpPositionArray,
+                        0,
+                        i - prevStart,
+                        font,
+                        tmpPaintForGlyph)
                 prevStart = i
                 arrayIndex = 0
             }
@@ -399,14 +404,13 @@
         }
 
         c.drawGlyphs(
-            line.glyphIds,
-            prevStart,
-            tmpPositionArray,
-            0,
-            run.end - prevStart,
-            font,
-            tmpPaintForGlyph
-        )
+                line.glyphIds,
+                prevStart,
+                tmpPositionArray,
+                0,
+                run.end - prevStart,
+                font,
+                tmpPaintForGlyph)
     }
 
     private fun updatePositionsAndFonts(
@@ -414,7 +418,9 @@
         updateBase: Boolean
     ) {
         // Update target positions with newly calculated text layout.
-        check(layoutResult.size == lines.size) { "The new layout result has different line count." }
+        check(layoutResult.size == lines.size) {
+            "The new layout result has different line count."
+        }
 
         lines.zip(layoutResult) { line, runs ->
             line.runs.zip(runs) { lineRun, newGlyphs ->
@@ -430,7 +436,7 @@
                         }
                         require(newFont === newGlyphs.getFont(i)) {
                             "The new layout has different font run." +
-                                " $newFont vs ${newGlyphs.getFont(i)} at $i"
+                                    " $newFont vs ${newGlyphs.getFont(i)} at $i"
                         }
                     }
 
@@ -438,7 +444,7 @@
                     // check new font can be interpolatable with base font.
                     require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
                         "New font cannot be interpolated with existing font. $newFont," +
-                            " ${run.baseFont}"
+                                " ${run.baseFont}"
                     }
 
                     if (updateBase) {
@@ -474,7 +480,10 @@
     }
 
     // Shape the text and stores the result to out argument.
-    private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> {
+    private fun shapeText(
+        layout: Layout,
+        paint: TextPaint
+    ): List<List<PositionedGlyphs>> {
         val out = mutableListOf<List<PositionedGlyphs>>()
         for (lineNo in 0 until layout.lineCount) { // Shape all lines.
             val lineStart = layout.getLineStart(lineNo)
@@ -486,13 +495,10 @@
             }
 
             val runs = mutableListOf<PositionedGlyphs>()
-            TextShaper.shapeText(
-                layout.text,
-                lineStart,
-                count,
-                layout.textDirectionHeuristic,
-                paint
-            ) { _, _, glyphs, _ -> runs.add(glyphs) }
+            TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
+                    paint) { _, _, glyphs, _ ->
+                runs.add(glyphs)
+            }
             out.add(runs)
         }
         return out
@@ -500,8 +506,8 @@
 }
 
 private fun Layout.getDrawOrigin(lineNo: Int) =
-    if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
-        getLineLeft(lineNo)
-    } else {
-        getLineRight(lineNo)
-    }
+        if (getParagraphDirection(lineNo) == Layout.DIR_LEFT_TO_RIGHT) {
+            getLineLeft(lineNo)
+        } else {
+            getLineRight(lineNo)
+        }
diff --git a/packages/SystemUI/checks/Android.bp b/packages/SystemUI/checks/Android.bp
index 40580d2..d3f66e3 100644
--- a/packages/SystemUI/checks/Android.bp
+++ b/packages/SystemUI/checks/Android.bp
@@ -37,12 +37,6 @@
 
 java_test_host {
     name: "SystemUILintCheckerTest",
-    // TODO(b/239881504): Since this test was written, Android
-    // Lint was updated, and now includes classes that were
-    // compiled for java 15. The soong build doesn't support
-    // java 15 yet, so we can't compile against "lint". Disable
-    // the test until java 15 is supported.
-    enabled: false,
     srcs: [
         "tests/**/*.kt",
         "tests/**/*.java",
@@ -59,5 +53,19 @@
     ],
     test_options: {
         unit_test: true,
+        tradefed_options: [
+            {
+                // lint bundles in some classes that were built with older versions
+                // of libraries, and no longer load. Since tradefed tries to load
+                // all classes in the jar to look for tests, it crashes loading them.
+                // Exclude these classes from tradefed's search.
+                name: "exclude-paths",
+                value: "org/apache",
+            },
+            {
+                name: "exclude-paths",
+                value: "META-INF",
+            },
+        ],
     },
 }
diff --git a/packages/SystemUI/res/drawable/overlay_badge_background.xml b/packages/SystemUI/res/drawable/overlay_badge_background.xml
index 857632e..53122c1 100644
--- a/packages/SystemUI/res/drawable/overlay_badge_background.xml
+++ b/packages/SystemUI/res/drawable/overlay_badge_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Android Open Source Project
+  ~ Copyright (C) 2022 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,8 +14,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        android:shape="oval">
-    <solid android:color="?androidprv:attr/colorSurface"/>
-</shape>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M0,0M48,48"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index a565988..d689828 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -148,9 +148,4 @@
         <include layout="@layout/ongoing_privacy_chip"/>
     </FrameLayout>
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:id="@+id/space"
-    />
 </com.android.systemui.util.NoRemeasureMotionLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 9fb84c8..496eb6e 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -93,13 +93,10 @@
         app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
     <ImageView
         android:id="@+id/screenshot_badge"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:padding="4dp"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:visibility="gone"
-        android:background="@drawable/overlay_badge_background"
         android:elevation="8dp"
-        android:src="@drawable/overlay_cancel"
         app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"
         app:layout_constraintEnd_toEndOf="@id/screenshot_preview_border"/>
     <FrameLayout
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index eca2b2a..d97031f 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -56,13 +56,9 @@
         <Layout
             android:layout_width="wrap_content"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constrainedWidth="true"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/space"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintTop_toBottomOf="@id/carrier_group"
-            app:layout_constraintHorizontal_bias="0"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
         />
     </Constraint>
 
@@ -87,39 +83,27 @@
     <Constraint
         android:id="@+id/statusIcons">
         <Layout
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constraintStart_toEndOf="@id/space"
+            app:layout_constraintWidth_default="wrap"
+            app:layout_constraintStart_toEndOf="@id/date"
             app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
             app:layout_constraintTop_toTopOf="@id/date"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="1"
+            app:layout_constraintBottom_toBottomOf="@id/date"
             />
     </Constraint>
 
     <Constraint
         android:id="@+id/batteryRemainingIcon">
         <Layout
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+            app:layout_constraintWidth_default="wrap"
             app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
-            app:layout_constraintStart_toEndOf="@id/statusIcons"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintTop_toTopOf="@id/date"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintHorizontal_bias="1"
-            app:layout_constraintHorizontal_chainStyle="spread_inside"
+            app:layout_constraintBottom_toBottomOf="@id/date"
             />
     </Constraint>
 
-
-    <Constraint
-        android:id="@id/space">
-        <Layout
-            android:layout_width="0dp"
-            android:layout_height="0dp"
-            app:layout_constraintStart_toEndOf="@id/date"
-            app:layout_constraintEnd_toStartOf="@id/statusIcons"
-            />
-    </Constraint>
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 0fc9ef9..632fcdc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -22,8 +22,6 @@
 import android.os.HandlerThread;
 import android.util.Log;
 
-import androidx.annotation.Nullable;
-
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
@@ -55,7 +53,6 @@
         mContext = context;
     }
 
-    @Nullable
     protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
 
     /**
@@ -72,11 +69,6 @@
      * Starts the initialization process. This stands up the Dagger graph.
      */
     public void init(boolean fromTest) throws ExecutionException, InterruptedException {
-        GlobalRootComponent.Builder globalBuilder = getGlobalRootComponentBuilder();
-        if (globalBuilder == null) {
-            return;
-        }
-
         mRootComponent = getGlobalRootComponentBuilder()
                 .context(mContext)
                 .instrumentationTest(fromTest)
@@ -127,7 +119,6 @@
                     .setBackAnimation(Optional.ofNullable(null))
                     .setDesktopMode(Optional.ofNullable(null));
         }
-
         mSysUIComponent = builder.build();
         if (initializeComponents) {
             mSysUIComponent.init();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
index 55c095b..8aa3040 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui
 
-import android.app.Application
 import android.content.Context
 import com.android.systemui.dagger.DaggerReferenceGlobalRootComponent
 import com.android.systemui.dagger.GlobalRootComponent
@@ -25,17 +24,7 @@
  * {@link SystemUIInitializer} that stands up AOSP SystemUI.
  */
 class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
-
-    override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder? {
-        return when (Application.getProcessName()) {
-            SCREENSHOT_CROSS_PROFILE_PROCESS -> null
-            else -> DaggerReferenceGlobalRootComponent.builder()
-        }
-    }
-
-    companion object {
-        private const val SYSTEMUI_PROCESS = "com.android.systemui"
-        private const val SCREENSHOT_CROSS_PROFILE_PROCESS =
-                "$SYSTEMUI_PROCESS:screenshot_cross_profile"
+    override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
+        return DaggerReferenceGlobalRootComponent.builder()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index e8e1f2e..e9ac840 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -176,7 +176,8 @@
     private @Classifier.InteractionType int mPriorInteractionType = Classifier.GENERIC;
 
     @Inject
-    public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
+    public BrightLineFalsingManager(
+            FalsingDataProvider falsingDataProvider,
             MetricsLogger metricsLogger,
             @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
             SingleTapClassifier singleTapClassifier, LongTapClassifier longTapClassifier,
@@ -399,7 +400,9 @@
                 || mDataProvider.isJustUnlockedWithFace()
                 || mDataProvider.isDocked()
                 || mAccessibilityManager.isTouchExplorationEnabled()
-                || mDataProvider.isA11yAction();
+                || mDataProvider.isA11yAction()
+                || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
+                    && !mDataProvider.isFolded());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 09ebeea..5f347c1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.classifier;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
@@ -42,6 +43,7 @@
     private final int mWidthPixels;
     private final int mHeightPixels;
     private BatteryController mBatteryController;
+    private final FoldStateListener mFoldStateListener;
     private final DockManager mDockManager;
     private final float mXdpi;
     private final float mYdpi;
@@ -65,12 +67,14 @@
     public FalsingDataProvider(
             DisplayMetrics displayMetrics,
             BatteryController batteryController,
+            FoldStateListener foldStateListener,
             DockManager dockManager) {
         mXdpi = displayMetrics.xdpi;
         mYdpi = displayMetrics.ydpi;
         mWidthPixels = displayMetrics.widthPixels;
         mHeightPixels = displayMetrics.heightPixels;
         mBatteryController = batteryController;
+        mFoldStateListener = foldStateListener;
         mDockManager = dockManager;
 
         FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi());
@@ -376,6 +380,10 @@
         return mBatteryController.isWirelessCharging() || mDockManager.isDocked();
     }
 
+    public boolean isFolded() {
+        return Boolean.TRUE.equals(mFoldStateListener.getFolded());
+    }
+
     /** Implement to be alerted abotu the beginning and ending of falsing tracking. */
     public interface SessionListener {
         /** Called when the lock screen is shown and falsing-tracking begins. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 52da3f8..d040f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -207,6 +207,9 @@
     val AUTO_PIN_CONFIRMATION =
         unreleasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation")
 
+    // TODO(b/262859270): Tracking Bug
+    @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6b121b8..18854e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -41,6 +41,7 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
+import android.app.BroadcastOptions;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.WindowConfiguration;
@@ -391,6 +392,12 @@
                     | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                     | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
 
+    private static final Bundle USER_PRESENT_INTENT_OPTIONS =
+            BroadcastOptions.makeBasic()
+                    .setDeferUntilActive(true)
+                    .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                    .toBundle();
+
     /**
      * {@link #setKeyguardEnabled} waits on this condition when it re-enables
      * the keyguard.
@@ -1921,13 +1928,23 @@
             return;
         }
 
-        // if the keyguard is already showing, don't bother. check flags in both files
-        // to account for the hiding animation which results in a delay and discrepancy
-        // between flags
+        // If the keyguard is already showing, see if we don't need to bother re-showing it. Check
+        // flags in both files to account for the hiding animation which results in a delay and
+        // discrepancy between flags.
         if (mShowing && mKeyguardStateController.isShowing()) {
-            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
-            resetStateLocked();
-            return;
+            if (mPM.isInteractive()) {
+                // It's already showing, and we're not trying to show it while the screen is off.
+                // We can simply reset all of the views.
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+                resetStateLocked();
+                return;
+            } else {
+                // We are trying to show the keyguard while the screen is off - this results from
+                // race conditions involving locking while unlocking. Don't short-circuit here and
+                // ensure the keyguard is fully re-shown.
+                Log.e(TAG,
+                        "doKeyguard: already showing, but re-showing since we're not interactive");
+            }
         }
 
         // In split system user mode, we never unlock system user.
@@ -2319,7 +2336,10 @@
                         Context.USER_SERVICE);
                 mUiBgExecutor.execute(() -> {
                     for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
-                        mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
+                        mContext.sendBroadcastAsUser(USER_PRESENT_INTENT,
+                                UserHandle.of(profileId),
+                                null,
+                                USER_PRESENT_INTENT_OPTIONS);
                     }
                     mLockPatternUtils.userPresent(currentUserId);
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 7cf63f6..1da30ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -36,7 +36,6 @@
     void removeCallback(Callback callback);
     void removeTile(String tileSpec);
     void removeTiles(Collection<String> specs);
-    void unmarkTileAsAutoAdded(String tileSpec);
 
     int indexOf(String tileSpec);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index cad296b..100853c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -427,11 +427,6 @@
         mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
     }
 
-    @Override
-    public void unmarkTileAsAutoAdded(String spec) {
-        if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
-    }
-
     /**
      * Add a tile to the end
      *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index a6c7781..72c6bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -101,7 +101,6 @@
     @MainThread
     public void onManagedProfileRemoved() {
         mHost.removeTile(getTileSpec());
-        mHost.unmarkTileAsAutoAdded(getTileSpec());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 91ebf79..b21a485 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -687,8 +687,8 @@
                     }
                 });
         if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
-            mScreenshotView.badgeScreenshot(
-                    mContext.getPackageManager().getUserBadgeForDensity(owner, 0));
+            mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
+                    mContext.getDrawable(R.drawable.overlay_badge_background), owner));
         }
         mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
         if (DEBUG_WINDOW) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 200288b..4dbe099 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -111,7 +111,7 @@
             // These get called when a managed profile goes in or out of quiet mode.
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
-
+            addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
             addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
         }
@@ -128,6 +128,7 @@
             Intent.ACTION_USER_INFO_CHANGED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+            Intent.ACTION_MANAGED_PROFILE_ADDED,
             Intent.ACTION_MANAGED_PROFILE_REMOVED,
             Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> {
                 handleProfilesChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 9070ead..149ec54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -154,9 +154,7 @@
         if (!mAutoTracker.isAdded(SAVER)) {
             mDataSaverController.addCallback(mDataSaverListener);
         }
-        if (!mAutoTracker.isAdded(WORK)) {
-            mManagedProfileController.addCallback(mProfileCallback);
-        }
+        mManagedProfileController.addCallback(mProfileCallback);
         if (!mAutoTracker.isAdded(NIGHT)
                 && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             mNightDisplayListener.setCallback(mNightDisplayCallback);
@@ -275,18 +273,18 @@
         return mCurrentUser.getIdentifier();
     }
 
-    public void unmarkTileAsAutoAdded(String tabSpec) {
-        mAutoTracker.setTileRemoved(tabSpec);
-    }
-
     private final ManagedProfileController.Callback mProfileCallback =
             new ManagedProfileController.Callback() {
                 @Override
                 public void onManagedProfileChanged() {
-                    if (mAutoTracker.isAdded(WORK)) return;
                     if (mManagedProfileController.hasActiveProfile()) {
+                        if (mAutoTracker.isAdded(WORK)) return;
                         mHost.addTile(WORK);
                         mAutoTracker.setTileAdded(WORK);
+                    } else {
+                        if (!mAutoTracker.isAdded(WORK)) return;
+                        mHost.removeTile(WORK);
+                        mAutoTracker.setTileRemoved(WORK);
                     }
                 }
 
@@ -429,7 +427,7 @@
                 initSafetyTile();
             } else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
                 mHost.removeTile(mSafetySpec);
-                mHost.unmarkTileAsAutoAdded(mSafetySpec);
+                mAutoTracker.setTileRemoved(mSafetySpec);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 59ad24a..2709da3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.unfold
 
 import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.LifecycleScreenStatusProvider
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.system.SystemUnfoldSharedModule
@@ -32,6 +35,7 @@
 import dagger.Module
 import dagger.Provides
 import java.util.Optional
+import java.util.concurrent.Executor
 import javax.inject.Named
 import javax.inject.Singleton
 
@@ -40,6 +44,20 @@
 
     @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
 
+    /** A globally available FoldStateListener that allows one to query the fold state. */
+    @Provides
+    @Singleton
+    fun providesFoldStateListener(
+        deviceStateManager: DeviceStateManager,
+        @Application context: Context,
+        @Main executor: Executor
+    ): DeviceStateManager.FoldStateListener {
+        val listener = DeviceStateManager.FoldStateListener(context)
+        deviceStateManager.registerCallback(executor, listener)
+
+        return listener
+    }
+
     @Provides
     @Singleton
     fun providesFoldStateLoggingProvider(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 0fadc13..e4df754 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -106,6 +106,7 @@
         mClassifiers.add(mClassifierB);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mFalsingDataProvider.isFolded()).thenReturn(true);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
@@ -121,6 +122,7 @@
         mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
         mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
         mFakeFeatureFlags.set(Flags.MEDIA_FALSING_PENALTY, true);
+        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 4281ee0..ae38eb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -89,25 +89,27 @@
         mClassifiers.add(mClassifierA);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mFalsingDataProvider.isFolded()).thenReturn(true);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
                 mAccessibilityManager, false, mFakeFeatureFlags);
         mFakeFeatureFlags.set(Flags.FALSING_FOR_LONG_TAPS, true);
+        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
     @Test
     public void testA11yDisablesGesture() {
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
     }
 
     @Test
     public void testA11yDisablesTap() {
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
         when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
     }
 
 
@@ -179,4 +181,11 @@
         when(mFalsingDataProvider.isA11yAction()).thenReturn(true);
         assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
     }
+
+    @Test
+    public void testSkipUnfolded() {
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        when(mFalsingDataProvider.isFolded()).thenReturn(false);
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
index 5fa7214..94cf384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.classifier;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
@@ -38,6 +39,7 @@
     private float mOffsetY = 0;
     @Mock
     private BatteryController mBatteryController;
+    private FoldStateListener mFoldStateListener = new FoldStateListener(mContext);
     private final DockManagerFake mDockManager = new DockManagerFake();
 
     public void setup() {
@@ -47,7 +49,8 @@
         displayMetrics.ydpi = 100;
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
-        mDataProvider = new FalsingDataProvider(displayMetrics, mBatteryController, mDockManager);
+        mDataProvider = new FalsingDataProvider(
+                displayMetrics, mBatteryController, mFoldStateListener, mDockManager);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index d315c2d..c451a1e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
 import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
@@ -50,6 +51,8 @@
     private FalsingDataProvider mDataProvider;
     @Mock
     private BatteryController mBatteryController;
+    @Mock
+    private FoldStateListener mFoldStateListener;
     private final DockManagerFake mDockManager = new DockManagerFake();
 
     @Before
@@ -61,7 +64,8 @@
         displayMetrics.ydpi = 100;
         displayMetrics.widthPixels = 1000;
         displayMetrics.heightPixels = 1000;
-        mDataProvider = new FalsingDataProvider(displayMetrics, mBatteryController, mDockManager);
+        mDataProvider = new FalsingDataProvider(
+                displayMetrics, mBatteryController, mFoldStateListener, mDockManager);
     }
 
     @After
@@ -316,4 +320,16 @@
         mDataProvider.onA11yAction();
         assertThat(mDataProvider.isA11yAction()).isTrue();
     }
+
+    @Test
+    public void test_FoldedState_Folded() {
+        when(mFoldStateListener.getFolded()).thenReturn(true);
+        assertThat(mDataProvider.isFolded()).isTrue();
+    }
+
+    @Test
+    public void test_FoldedState_Unfolded() {
+        when(mFoldStateListener.getFolded()).thenReturn(false);
+        assertThat(mDataProvider.isFolded()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 59e4655..7c20e3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -485,6 +486,38 @@
         assertTrue(mViewMediator.isShowingAndNotOccluded());
     }
 
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testDoKeyguardWhileInteractive_resets() {
+        mViewMediator.setShowingLocked(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mPowerManager.isInteractive()).thenReturn(true);
+
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+        verify(mStatusBarKeyguardViewManager).reset(anyBoolean());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testDoKeyguardWhileNotInteractive_showsInsteadOfResetting() {
+        mViewMediator.setShowingLocked(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mPowerManager.isInteractive()).thenReturn(false);
+
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+        verify(mStatusBarKeyguardViewManager, never()).reset(anyBoolean());
+    }
+
     private void createAndStartViewMediator() {
         mViewMediator = new KeyguardViewMediator(
                 mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
new file mode 100644
index 0000000..3710281
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.settings
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.concurrent.futures.DirectExecutor
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(Parameterized::class)
+class UserTrackerImplReceiveTest : SysuiTestCase() {
+
+    companion object {
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun data(): Iterable<String> =
+            listOf(
+                Intent.ACTION_USER_INFO_CHANGED,
+                Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
+                Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
+                Intent.ACTION_MANAGED_PROFILE_ADDED,
+                Intent.ACTION_MANAGED_PROFILE_REMOVED,
+                Intent.ACTION_MANAGED_PROFILE_UNLOCKED
+            )
+    }
+
+    private val executor: Executor = DirectExecutor.INSTANCE
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var userManager: UserManager
+    @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
+    @Mock(stubOnly = true) private lateinit var handler: Handler
+
+    @Parameterized.Parameter lateinit var intentAction: String
+    @Mock private lateinit var callback: UserTracker.Callback
+    @Captor private lateinit var captor: ArgumentCaptor<List<UserInfo>>
+
+    private lateinit var tracker: UserTrackerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(context.user).thenReturn(UserHandle.SYSTEM)
+        `when`(context.createContextAsUser(ArgumentMatchers.any(), anyInt())).thenReturn(context)
+
+        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+    }
+
+    @Test
+    fun `calls callback and updates profiles when an intent received`() {
+        tracker.initialize(0)
+        tracker.addCallback(callback, executor)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile =
+                UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+                )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        tracker.onReceive(context, Intent(intentAction))
+
+        verify(callback, times(0)).onUserChanged(anyInt(), any())
+        verify(callback, times(1)).onProfilesChanged(capture(captor))
+        assertThat(captor.value.map { it.id }).containsExactly(0, profileID)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 52462c7..e65bbb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -124,6 +124,16 @@
 
         verify(context).registerReceiverForAllUsers(
                 eq(tracker), capture(captor), isNull(), eq(handler))
+        with(captor.value) {
+            assertThat(countActions()).isEqualTo(7)
+            assertThat(hasAction(Intent.ACTION_USER_SWITCHED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
+            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+        }
     }
 
     @Test
@@ -280,37 +290,6 @@
     }
 
     @Test
-    fun testCallbackCalledOnProfileChanged() {
-        tracker.initialize(0)
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-        val profileID = tracker.userId + 10
-
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
-        }
-
-        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-
-        tracker.onReceive(context, intent)
-
-        assertThat(callback.calledOnUserChanged).isEqualTo(0)
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
-        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
-    }
-
-    @Test
     fun testCallbackCalledOnUserInfoChanged() {
         tracker.initialize(0)
         val callback = TestCallback()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index 88651c1..f802a5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.testing.AndroidTestingRunner
+import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
@@ -92,12 +93,12 @@
             assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
 
             assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
-            assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0f)
+            assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd)
                 .isEqualTo(PARENT_ID)
             assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias)
-                .isEqualTo(1f)
+                .isEqualTo(0.5f)
 
             assertThat(getConstraint(R.id.privacy_container).layout.endToEnd)
                 .isEqualTo(R.id.end_guide)
@@ -331,10 +332,8 @@
         val views = mapOf(
                 R.id.clock to "clock",
                 R.id.date to "date",
-                R.id.statusIcons to "icons",
                 R.id.privacy_container to "privacy",
                 R.id.carrier_group to "carriers",
-                R.id.batteryRemainingIcon to "battery",
         )
         views.forEach { (id, name) ->
             assertWithMessage("$name has 0 height in qqs")
@@ -352,11 +351,8 @@
     fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() {
         val views = mapOf(
                 R.id.clock to "clock",
-                R.id.date to "date",
-                R.id.statusIcons to "icons",
                 R.id.privacy_container to "privacy",
                 R.id.carrier_group to "carriers",
-                R.id.batteryRemainingIcon to "battery",
         )
         views.forEach { (id, name) ->
             expect.withMessage("$name changes height")
@@ -369,8 +365,8 @@
     }
 
     private fun Int.fromConstraint() = when (this) {
-        -1 -> "MATCH_PARENT"
-        -2 -> "WRAP_CONTENT"
+        ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT"
+        ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT"
         else -> toString()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 4ccbc6d..091bb54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNotNull;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doReturn;
@@ -74,6 +75,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.util.Collections;
 import java.util.List;
@@ -115,8 +117,10 @@
     @Spy private PackageManager mPackageManager;
     private final boolean mIsReduceBrightColorsAvailable = true;
 
-    private AutoTileManager mAutoTileManager;
+    private AutoTileManager mAutoTileManager; // under test
+
     private SecureSettings mSecureSettings;
+    private ManagedProfileController.Callback mManagedProfileCallback;
 
     @Before
     public void setUp() throws Exception {
@@ -303,7 +307,7 @@
 
         InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
         inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
+        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
 
         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
             InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
@@ -504,6 +508,40 @@
     }
 
     @Test
+    public void managedProfileAdded_tileAdded() {
+        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
+        mAutoTileManager = createAutoTileManager(mContext);
+        Mockito.doAnswer((Answer<Object>) invocation -> {
+            mManagedProfileCallback = invocation.getArgument(0);
+            return null;
+        }).when(mManagedProfileController).addCallback(any());
+        mAutoTileManager.init();
+        when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
+
+        mManagedProfileCallback.onManagedProfileChanged();
+
+        verify(mQsTileHost, times(1)).addTile(eq("work"));
+        verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
+    }
+
+    @Test
+    public void managedProfileRemoved_tileRemoved() {
+        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
+        mAutoTileManager = createAutoTileManager(mContext);
+        Mockito.doAnswer((Answer<Object>) invocation -> {
+            mManagedProfileCallback = invocation.getArgument(0);
+            return null;
+        }).when(mManagedProfileController).addCallback(any());
+        mAutoTileManager.init();
+        when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
+
+        mManagedProfileCallback.onManagedProfileChanged();
+
+        verify(mQsTileHost, times(1)).removeTile(eq("work"));
+        verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
+    }
+
+    @Test
     public void testEmptyArray_doesNotCrash() {
         mContext.getOrCreateTestableResources().addOverride(
                 R.array.config_quickSettingsAutoAdd, new String[0]);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 71a4c73..b41664f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -189,15 +189,20 @@
     private long mLastBatteryLevelChangedSentMs;
 
     private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
-            new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).toBundle();
+            new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true)
+            .toBundle();
     private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
-            new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).toBundle();
+            new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true)
+            .toBundle();
     private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
-            new IntentFilter(Intent.ACTION_POWER_CONNECTED)).toBundle();
+            new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true)
+            .toBundle();
     private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
-            new IntentFilter(Intent.ACTION_BATTERY_OKAY)).toBundle();
+            new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true)
+            .toBundle();
     private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
-            new IntentFilter(Intent.ACTION_BATTERY_LOW)).toBundle();
+            new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true)
+            .toBundle();
 
     private MetricsLogger mMetricsLogger;
 
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 819c948..68722d2 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -531,27 +531,30 @@
                         pw.println("|--> Pre-installed package install location: "
                                 + origPackageFilepath);
 
-                        if (useSha256) {
-                            String sha256Digest = PackageUtils.computeSha256DigestForLargeFile(
-                                    origPackageFilepath, PackageUtils.createLargeFileBuffer());
-                            pw.println("|--> Pre-installed package SHA-256 digest: "
-                                    + sha256Digest);
-                        }
+                        if (!origPackageFilepath.equals(APEX_PRELOAD_LOCATION_ERROR)) {
+                            if (useSha256) {
+                                String sha256Digest = PackageUtils.computeSha256DigestForLargeFile(
+                                        origPackageFilepath, PackageUtils.createLargeFileBuffer());
+                                pw.println("|--> Pre-installed package SHA-256 digest: "
+                                        + sha256Digest);
+                            }
 
-
-                        Map<Integer, byte[]> contentDigests = computeApkContentDigest(
-                                origPackageFilepath);
-                        if (contentDigests == null) {
-                            pw.println("ERROR: Failed to compute package content digest for "
-                                    + origPackageFilepath);
-                        } else {
-                            for (Map.Entry<Integer, byte[]> entry : contentDigests.entrySet()) {
-                                Integer algorithmId = entry.getKey();
-                                byte[] contentDigest = entry.getValue();
-                                pw.println("|--> Pre-installed package content digest: "
-                                        + HexEncoding.encodeToString(contentDigest, false));
-                                pw.println("|--> Pre-installed package content digest algorithm: "
-                                        + translateContentDigestAlgorithmIdToString(algorithmId));
+                            Map<Integer, byte[]> contentDigests = computeApkContentDigest(
+                                    origPackageFilepath);
+                            if (contentDigests == null) {
+                                pw.println("|--> ERROR: Failed to compute package content digest "
+                                        + "for " + origPackageFilepath);
+                            } else {
+                                for (Map.Entry<Integer, byte[]> entry : contentDigests.entrySet()) {
+                                    Integer algorithmId = entry.getKey();
+                                    byte[] contentDigest = entry.getValue();
+                                    pw.println("|--> Pre-installed package content digest: "
+                                            + HexEncoding.encodeToString(contentDigest, false));
+                                    pw.println("|--> Pre-installed package content digest "
+                                            + "algorithm: "
+                                            + translateContentDigestAlgorithmIdToString(
+                                                    algorithmId));
+                                }
                             }
                         }
                     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d478dca..1bc312e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -159,6 +159,8 @@
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionMethod;
+import android.annotation.PermissionName;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityClient;
@@ -251,8 +253,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionInfo;
-import android.content.pm.PermissionMethod;
-import android.content.pm.PermissionName;
 import android.content.pm.ProcessInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ProviderInfoList;
@@ -14108,8 +14108,16 @@
         if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                 (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
                 + " ordered=" + ordered + " userid=" + userId);
-        if ((resultTo != null) && !ordered && !mEnableModernQueue) {
-            Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
+        if ((resultTo != null) && !ordered) {
+            if (!mEnableModernQueue) {
+                Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
+            }
+            if (!UserHandle.isCore(callingUid)) {
+                String msg = "Unauthorized unordered resultTo broadcast "
+                             + intent + " sent from uid " + callingUid;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
         }
 
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
@@ -14190,6 +14198,18 @@
             }
         }
 
+        // resultTo broadcasts are always infinitely deferrable.
+        if ((resultTo != null) && !ordered && mEnableModernQueue) {
+            if (brOptions == null) {
+                brOptions = BroadcastOptions.makeBasic();
+            }
+            brOptions.setDeferUntilActive(true);
+        }
+
+        if (ordered && brOptions != null && brOptions.isDeferUntilActive()) {
+            throw new IllegalArgumentException("Ordered broadcasts can't be deferred until active");
+        }
+
         // Verify that protected broadcasts are only being sent by system code,
         // and that system code is only sending protected broadcasts.
         final boolean isProtectedBroadcast;
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index fa7748b..41d9a11 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -62,6 +62,7 @@
  */
 // @NotThreadSafe
 class BroadcastProcessQueue {
+    static final boolean VERBOSE = false;
     final @NonNull BroadcastConstants constants;
     final @NonNull String processName;
     final int uid;
@@ -168,10 +169,14 @@
     /**
      * Count of pending broadcasts of these various flavors.
      */
+    private int mCountEnqueued;
+    private int mCountDeferred;
     private int mCountForeground;
+    private int mCountForegroundDeferred;
     private int mCountOrdered;
     private int mCountAlarm;
     private int mCountPrioritized;
+    private int mCountPrioritizedDeferred;
     private int mCountInteractive;
     private int mCountResultTo;
     private int mCountInstrumented;
@@ -226,10 +231,10 @@
      * used for ordered broadcasts and priority traunches.
      */
     public void enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
-            @NonNull BroadcastConsumer replacedBroadcastConsumer) {
+            @NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
         if (record.isReplacePending()) {
             final boolean didReplace = replaceBroadcast(record, recordIndex,
-                    replacedBroadcastConsumer);
+                    replacedBroadcastConsumer, wouldBeSkipped);
             if (didReplace) {
                 return;
             }
@@ -240,13 +245,14 @@
         SomeArgs newBroadcastArgs = SomeArgs.obtain();
         newBroadcastArgs.arg1 = record;
         newBroadcastArgs.argi1 = recordIndex;
+        newBroadcastArgs.argi2 = (wouldBeSkipped ? 1 : 0);
 
         // Cross-broadcast prioritization policy:  some broadcasts might warrant being
         // issued ahead of others that are already pending, for example if this new
         // broadcast is in a different delivery class or is tied to a direct user interaction
         // with implicit responsiveness expectations.
         getQueueForBroadcast(record).addLast(newBroadcastArgs);
-        onBroadcastEnqueued(record, recordIndex);
+        onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
     }
 
     /**
@@ -258,11 +264,12 @@
      * {@code false} otherwise.
      */
     private boolean replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
-            @NonNull BroadcastConsumer replacedBroadcastConsumer) {
+            @NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
         final int count = mPendingQueues.size();
         for (int i = 0; i < count; ++i) {
             final ArrayDeque<SomeArgs> queue = mPendingQueues.get(i);
-            if (replaceBroadcastInQueue(queue, record, recordIndex, replacedBroadcastConsumer)) {
+            if (replaceBroadcastInQueue(queue, record, recordIndex,
+                    replacedBroadcastConsumer, wouldBeSkipped)) {
                 return true;
             }
         }
@@ -279,13 +286,15 @@
      */
     private boolean replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
             @NonNull BroadcastRecord record, int recordIndex,
-            @NonNull BroadcastConsumer replacedBroadcastConsumer) {
+            @NonNull BroadcastConsumer replacedBroadcastConsumer,
+            boolean wouldBeSkipped) {
         final Iterator<SomeArgs> it = queue.descendingIterator();
         final Object receiver = record.receivers.get(recordIndex);
         while (it.hasNext()) {
             final SomeArgs args = it.next();
             final BroadcastRecord testRecord = (BroadcastRecord) args.arg1;
             final int testRecordIndex = args.argi1;
+            final boolean testWouldBeSkipped = (args.argi2 == 1);
             final Object testReceiver = testRecord.receivers.get(testRecordIndex);
             if ((record.callingUid == testRecord.callingUid)
                     && (record.userId == testRecord.userId)
@@ -295,9 +304,10 @@
                 // Exact match found; perform in-place swap
                 args.arg1 = record;
                 args.argi1 = recordIndex;
+                args.argi2 = (wouldBeSkipped ? 1 : 0);
                 record.copyEnqueueTimeFrom(testRecord);
-                onBroadcastDequeued(testRecord, testRecordIndex);
-                onBroadcastEnqueued(record, recordIndex);
+                onBroadcastDequeued(testRecord, testRecordIndex, testWouldBeSkipped);
+                onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
                 replacedBroadcastConsumer.accept(testRecord, testRecordIndex);
                 return true;
             }
@@ -352,12 +362,13 @@
             final SomeArgs args = it.next();
             final BroadcastRecord record = (BroadcastRecord) args.arg1;
             final int recordIndex = args.argi1;
+            final boolean recordWouldBeSkipped = (args.argi2 == 1);
             if (predicate.test(record, recordIndex)) {
                 consumer.accept(record, recordIndex);
                 if (andRemove) {
                     args.recycle();
                     it.remove();
-                    onBroadcastDequeued(record, recordIndex);
+                    onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
                 }
                 didSomething = true;
             }
@@ -423,7 +434,7 @@
     }
 
     public int getPreferredSchedulingGroupLocked() {
-        if (mCountForeground > 0) {
+        if (mCountForeground > mCountForegroundDeferred) {
             // We have a foreground broadcast somewhere down the queue, so
             // boost priority until we drain them all
             return ProcessList.SCHED_GROUP_DEFAULT;
@@ -469,10 +480,11 @@
         final SomeArgs next = removeNextBroadcast();
         mActive = (BroadcastRecord) next.arg1;
         mActiveIndex = next.argi1;
+        final boolean wouldBeSkipped = (next.argi2 == 1);
         mActiveCountSinceIdle++;
         mActiveViaColdStart = false;
         next.recycle();
-        onBroadcastDequeued(mActive, mActiveIndex);
+        onBroadcastDequeued(mActive, mActiveIndex, wouldBeSkipped);
     }
 
     /**
@@ -489,8 +501,16 @@
     /**
      * Update summary statistics when the given record has been enqueued.
      */
-    private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex) {
+    private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex,
+            boolean wouldBeSkipped) {
+        mCountEnqueued++;
+        if (record.deferUntilActive) {
+            mCountDeferred++;
+        }
         if (record.isForeground()) {
+            if (record.deferUntilActive) {
+                mCountForegroundDeferred++;
+            }
             mCountForeground++;
         }
         if (record.ordered) {
@@ -500,6 +520,9 @@
             mCountAlarm++;
         }
         if (record.prioritized) {
+            if (record.deferUntilActive) {
+                mCountPrioritizedDeferred++;
+            }
             mCountPrioritized++;
         }
         if (record.interactive) {
@@ -511,7 +534,8 @@
         if (record.callerInstrumented) {
             mCountInstrumented++;
         }
-        if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
+        if (!wouldBeSkipped
+                && (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
             mCountManifest++;
         }
         invalidateRunnableAt();
@@ -520,8 +544,16 @@
     /**
      * Update summary statistics when the given record has been dequeued.
      */
-    private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex) {
+    private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex,
+            boolean wouldBeSkipped) {
+        mCountEnqueued--;
+        if (record.deferUntilActive) {
+            mCountDeferred--;
+        }
         if (record.isForeground()) {
+            if (record.deferUntilActive) {
+                mCountForegroundDeferred--;
+            }
             mCountForeground--;
         }
         if (record.ordered) {
@@ -531,6 +563,9 @@
             mCountAlarm--;
         }
         if (record.prioritized) {
+            if (record.deferUntilActive) {
+                mCountPrioritizedDeferred--;
+            }
             mCountPrioritized--;
         }
         if (record.interactive) {
@@ -542,7 +577,8 @@
         if (record.callerInstrumented) {
             mCountInstrumented--;
         }
-        if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
+        if (!wouldBeSkipped
+                && (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
             mCountManifest--;
         }
         invalidateRunnableAt();
@@ -741,6 +777,15 @@
         return mRunnableAt != Long.MAX_VALUE;
     }
 
+    public boolean isDeferredUntilActive() {
+        if (mRunnableAtInvalidated) updateRunnableAt();
+        return mRunnableAtReason == BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER;
+    }
+
+    public boolean hasDeferredBroadcasts() {
+        return (mCountDeferred > 0);
+    }
+
     /**
      * Return time at which this process is considered runnable. This is
      * typically the time at which the next pending broadcast was first
@@ -776,6 +821,7 @@
     static final int REASON_INSTRUMENTED = 5;
     static final int REASON_PERSISTENT = 6;
     static final int REASON_FORCE_DELAYED = 7;
+    static final int REASON_CACHED_INFINITE_DEFER = 8;
     static final int REASON_CONTAINS_FOREGROUND = 10;
     static final int REASON_CONTAINS_ORDERED = 11;
     static final int REASON_CONTAINS_ALARM = 12;
@@ -794,6 +840,7 @@
             REASON_INSTRUMENTED,
             REASON_PERSISTENT,
             REASON_FORCE_DELAYED,
+            REASON_CACHED_INFINITE_DEFER,
             REASON_CONTAINS_FOREGROUND,
             REASON_CONTAINS_ORDERED,
             REASON_CONTAINS_ALARM,
@@ -816,6 +863,7 @@
             case REASON_INSTRUMENTED: return "INSTRUMENTED";
             case REASON_PERSISTENT: return "PERSISTENT";
             case REASON_FORCE_DELAYED: return "FORCE_DELAYED";
+            case REASON_CACHED_INFINITE_DEFER: return "INFINITE_DEFER";
             case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
             case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
             case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
@@ -831,9 +879,16 @@
     private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) {
         final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];
 
+        int existingDeferredCount = 0;
+        if (r.deferUntilActive) {
+            for (int i = 0; i < index; i++) {
+                if (r.deferredUntilActive[i]) existingDeferredCount++;
+            }
+        }
+
         // We might be blocked waiting for other receivers to finish,
         // typically for an ordered broadcast or priority traunches
-        if (r.terminalCount < blockedUntilTerminalCount
+        if ((r.terminalCount + existingDeferredCount) < blockedUntilTerminalCount
                 && !isDeliveryStateTerminal(r.getDeliveryState(index))) {
             return true;
         }
@@ -862,7 +917,7 @@
             if (mForcedDelayedDurationMs > 0) {
                 mRunnableAt = runnableAt + mForcedDelayedDurationMs;
                 mRunnableAtReason = REASON_FORCE_DELAYED;
-            } else if (mCountForeground > 0) {
+            } else if (mCountForeground > mCountForegroundDeferred) {
                 mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                 mRunnableAtReason = REASON_CONTAINS_FOREGROUND;
             } else if (mCountInteractive > 0) {
@@ -880,12 +935,9 @@
             } else if (mCountAlarm > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_ALARM;
-            } else if (mCountPrioritized > 0) {
+            } else if (mCountPrioritized > mCountPrioritizedDeferred) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;
-            } else if (mCountResultTo > 0) {
-                mRunnableAt = runnableAt;
-                mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
             } else if (mCountManifest > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_MANIFEST;
@@ -893,8 +945,39 @@
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_PERSISTENT;
             } else if (mProcessCached) {
-                mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
-                mRunnableAtReason = REASON_CACHED;
+                if (r.deferUntilActive) {
+                    // All enqueued broadcasts are deferrable, defer
+                    if (mCountDeferred == mCountEnqueued) {
+                        mRunnableAt = Long.MAX_VALUE;
+                        mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;
+                    } else {
+                        // At least one enqueued broadcast isn't deferrable, repick time and reason
+                        // for this record. If a later record is not deferrable and is one of these
+                        // special cases, one of the cases above would have already caught that.
+                        if (r.isForeground()) {
+                            mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
+                            mRunnableAtReason = REASON_CONTAINS_FOREGROUND;
+                        } else if (r.prioritized) {
+                            mRunnableAt = runnableAt;
+                            mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;
+                        } else if (r.resultTo != null) {
+                            mRunnableAt = runnableAt;
+                            mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
+                        } else {
+                            mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
+                            mRunnableAtReason = REASON_CACHED;
+                        }
+                    }
+                } else {
+                    // This record isn't deferrable
+                    mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
+                    mRunnableAtReason = REASON_CACHED;
+                }
+            } else if (mCountResultTo > 0) {
+                // All resultTo broadcasts are infinitely deferrable, so if the app
+                // is already cached, they'll be deferred on the line above
+                mRunnableAt = runnableAt;
+                mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
             } else {
                 mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
                 mRunnableAtReason = REASON_NORMAL;
@@ -907,6 +990,15 @@
                 mRunnableAt = Math.min(mRunnableAt, runnableAt);
                 mRunnableAtReason = REASON_MAX_PENDING;
             }
+
+            if (VERBOSE) {
+                Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, "BroadcastQueue",
+                        ((app != null) ? app.processName : "(null)")
+                        + ":" + r.intent.toString() + ":"
+                        + r.deferUntilActive
+                        + ":" + mRunnableAt + " " + reasonToString(mRunnableAtReason)
+                        + ":" + ((app != null) ? app.isCached() : "false"));
+            }
         } else {
             mRunnableAt = Long.MAX_VALUE;
             mRunnableAtReason = REASON_EMPTY;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index a994b1d..d819bfc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -385,6 +385,8 @@
         // If app isn't running, and there's nothing in the queue, clean up
         if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
             removeProcessQueue(queue.processName, queue.uid);
+        } else {
+            updateQueueDeferred(queue);
         }
     }
 
@@ -637,11 +639,34 @@
         final ArraySet<BroadcastRecord> replacedBroadcasts = new ArraySet<>();
         final BroadcastConsumer replacedBroadcastConsumer =
                 (record, i) -> replacedBroadcasts.add(record);
+        boolean enqueuedBroadcast = false;
+
         for (int i = 0; i < r.receivers.size(); i++) {
             final Object receiver = r.receivers.get(i);
             final BroadcastProcessQueue queue = getOrCreateProcessQueue(
                     getReceiverProcessName(receiver), getReceiverUid(receiver));
-            queue.enqueueOrReplaceBroadcast(r, i, replacedBroadcastConsumer);
+
+            boolean wouldBeSkipped = false;
+            if (receiver instanceof ResolveInfo) {
+                // If the app is running but would not have been started if the process weren't
+                // running, we're going to deliver the broadcast but mark that it's not a manifest
+                // broadcast that would have started the app. This allows BroadcastProcessQueue to
+                // defer the broadcast as though it were a normal runtime receiver.
+                wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
+                if (wouldBeSkipped && queue.app == null && !queue.getActiveViaColdStart()) {
+                    // Skip receiver if there's no running app, the app is not being started, and
+                    // the app wouldn't be launched for this broadcast
+                    setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
+                            "skipped by policy to avoid cold start");
+                    continue;
+                }
+            }
+            enqueuedBroadcast = true;
+            queue.enqueueOrReplaceBroadcast(r, i, replacedBroadcastConsumer, wouldBeSkipped);
+            if (r.isDeferUntilActive() && queue.isDeferredUntilActive()) {
+                setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
+                        "deferred at enqueue time");
+            }
             updateRunnableList(queue);
             enqueueUpdateRunningList();
         }
@@ -651,7 +676,7 @@
         skipAndCancelReplacedBroadcasts(replacedBroadcasts);
 
         // If nothing to dispatch, send any pending result immediately
-        if (r.receivers.isEmpty()) {
+        if (r.receivers.isEmpty() || !enqueuedBroadcast) {
             scheduleResultTo(r);
             notifyFinishBroadcast(r);
         }
@@ -1195,11 +1220,19 @@
             @NonNull Object receiver, @DeliveryState int newDeliveryState, String reason) {
         final int cookie = traceBegin("setDeliveryState");
         final int oldDeliveryState = getDeliveryState(r, index);
+        boolean checkFinished = false;
 
         // Only apply state when we haven't already reached a terminal state;
         // this is how we ignore racing timeout messages
         if (!isDeliveryStateTerminal(oldDeliveryState)) {
             r.setDeliveryState(index, newDeliveryState);
+            if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
+                r.deferredCount--;
+            } else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
+                // If we're deferring a broadcast, maybe that's enough to unblock the final callback
+                r.deferredCount++;
+                checkFinished = true;
+            }
         }
 
         // Emit any relevant tracing results when we're changing the delivery
@@ -1217,7 +1250,8 @@
         // bookkeeping to update for ordered broadcasts
         if (!isDeliveryStateTerminal(oldDeliveryState)
                 && isDeliveryStateTerminal(newDeliveryState)) {
-            if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
+            if (DEBUG_BROADCAST
+                    && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
                 logw("Delivery state of " + r + " to " + receiver
                         + " via " + app + " changed from "
                         + deliveryStateToString(oldDeliveryState) + " to "
@@ -1226,9 +1260,12 @@
 
             r.terminalCount++;
             notifyFinishReceiver(queue, r, index, receiver);
-
-            // When entire ordered broadcast finished, deliver final result
-            final boolean recordFinished = (r.terminalCount == r.receivers.size());
+            checkFinished = true;
+        }
+        // When entire ordered broadcast finished, deliver final result
+        if (checkFinished) {
+            final boolean recordFinished =
+                    ((r.terminalCount + r.deferredCount) == r.receivers.size());
             if (recordFinished) {
                 scheduleResultTo(r);
             }
@@ -1329,6 +1366,16 @@
         r.resultExtras = null;
     };
 
+    private final BroadcastConsumer mBroadcastConsumerDefer = (r, i) -> {
+        setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
+                "mBroadcastConsumerDefer");
+    };
+
+    private final BroadcastConsumer mBroadcastConsumerUndoDefer = (r, i) -> {
+        setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
+                "mBroadcastConsumerUndoDefer");
+    };
+
     /**
      * Verify that all known {@link #mProcessQueues} are in the state tested by
      * the given {@link Predicate}.
@@ -1392,6 +1439,21 @@
         }
     }
 
+    private void updateQueueDeferred(
+            @NonNull BroadcastProcessQueue leaf) {
+        if (leaf.isDeferredUntilActive()) {
+            leaf.forEachMatchingBroadcast((r, i) -> {
+                return r.deferUntilActive && (r.getDeliveryState(i)
+                        == BroadcastRecord.DELIVERY_PENDING);
+            }, mBroadcastConsumerDefer, false);
+        } else if (leaf.hasDeferredBroadcasts()) {
+            leaf.forEachMatchingBroadcast((r, i) -> {
+                return r.deferUntilActive && (r.getDeliveryState(i)
+                        == BroadcastRecord.DELIVERY_DEFERRED);
+            }, mBroadcastConsumerUndoDefer, false);
+        }
+    }
+
     @Override
     public void start(@NonNull ContentResolver resolver) {
         mFgConstants.startObserving(mHandler, resolver);
@@ -1404,6 +1466,7 @@
                     BroadcastProcessQueue leaf = mProcessQueues.get(uid);
                     while (leaf != null) {
                         leaf.setProcessCached(cached);
+                        updateQueueDeferred(leaf);
                         updateRunnableList(leaf);
                         leaf = leaf.processNameNext;
                     }
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1d4d425..9679fb8 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -88,6 +88,7 @@
     final boolean interactive; // originated from user interaction?
     final boolean initialSticky; // initial broadcast from register to sticky?
     final boolean prioritized; // contains more than one priority tranche
+    final boolean deferUntilActive; // infinitely deferrable broadcast
     final int userId;       // user id this broadcast was for
     final @Nullable String resolvedType; // the resolved data type
     final @Nullable String[] requiredPermissions; // permissions the caller has required
@@ -97,6 +98,7 @@
     final @Nullable BroadcastOptions options; // BroadcastOptions supplied by caller
     final @NonNull List<Object> receivers;   // contains BroadcastFilter and ResolveInfo
     final @DeliveryState int[] delivery;   // delivery state of each receiver
+    final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
     final int[] blockedUntilTerminalCount; // blocked until count of each receiver
     @Nullable ProcessRecord resultToApp; // who receives final result if non-null
     @Nullable IIntentReceiver resultTo; // who receives final result if non-null
@@ -130,6 +132,7 @@
     int manifestCount;      // number of manifest receivers dispatched.
     int manifestSkipCount;  // number of manifest receivers skipped.
     int terminalCount;      // number of receivers in terminal state.
+    int deferredCount;      // number of receivers in deferred state.
     @Nullable BroadcastQueue queue;   // the outbound queue handling this broadcast
 
     // if set to true, app's process will be temporarily allowed to start activities from background
@@ -168,6 +171,8 @@
     static final int DELIVERY_SCHEDULED = 4;
     /** Terminal state: failure to dispatch */
     static final int DELIVERY_FAILURE = 5;
+    /** Intermediate state: currently deferred while app is cached */
+    static final int DELIVERY_DEFERRED = 6;
 
     @IntDef(flag = false, prefix = { "DELIVERY_" }, value = {
             DELIVERY_PENDING,
@@ -176,6 +181,7 @@
             DELIVERY_TIMEOUT,
             DELIVERY_SCHEDULED,
             DELIVERY_FAILURE,
+            DELIVERY_DEFERRED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeliveryState {}
@@ -188,6 +194,7 @@
             case DELIVERY_TIMEOUT: return "TIMEOUT";
             case DELIVERY_SCHEDULED: return "SCHEDULED";
             case DELIVERY_FAILURE: return "FAILURE";
+            case DELIVERY_DEFERRED: return "DEFERRED";
             default: return Integer.toString(deliveryState);
         }
     }
@@ -388,6 +395,8 @@
         options = _options;
         receivers = (_receivers != null) ? _receivers : EMPTY_RECEIVERS;
         delivery = new int[_receivers != null ? _receivers.size() : 0];
+        deferUntilActive = options != null ? options.isDeferUntilActive() : false;
+        deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
         blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
         scheduledTime = new long[delivery.length];
         terminalTime = new long[delivery.length];
@@ -443,6 +452,8 @@
         options = from.options;
         receivers = from.receivers;
         delivery = from.delivery;
+        deferUntilActive = from.deferUntilActive;
+        deferredUntilActive = from.deferredUntilActive;
         blockedUntilTerminalCount = from.blockedUntilTerminalCount;
         scheduledTime = from.scheduledTime;
         terminalTime = from.terminalTime;
@@ -606,7 +617,7 @@
      */
     void setDeliveryState(int index, @DeliveryState int deliveryState) {
         delivery[index] = deliveryState;
-
+        if (deferUntilActive) deferredUntilActive[index] = false;
         switch (deliveryState) {
             case DELIVERY_DELIVERED:
             case DELIVERY_SKIPPED:
@@ -617,6 +628,9 @@
             case DELIVERY_SCHEDULED:
                 scheduledTime[index] = SystemClock.uptimeMillis();
                 break;
+            case DELIVERY_DEFERRED:
+                if (deferUntilActive) deferredUntilActive[index] = true;
+                break;
         }
     }
 
@@ -647,6 +661,10 @@
         return (intent.getFlags() & Intent.FLAG_RECEIVER_OFFLOAD) != 0;
     }
 
+    boolean isDeferUntilActive() {
+        return deferUntilActive;
+    }
+
     /**
      * Core policy determination about this broadcast's delivery prioritization
      */
diff --git a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
index 481ab17..6718319 100644
--- a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
+++ b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
@@ -229,12 +229,7 @@
                 return "Background execution disabled: receiving "
                         + r.intent + " to "
                         + component.flattenToShortString();
-            } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
-                    || (r.intent.getComponent() == null
-                        && r.intent.getPackage() == null
-                        && ((r.intent.getFlags()
-                                & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
-                        && !isSignaturePerm(r.requiredPermissions))) {
+            } else if (disallowBackgroundStart(r)) {
                 mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
                         component.getPackageName());
                 return "Background execution not allowed: receiving "
@@ -341,6 +336,18 @@
     }
 
     /**
+     * Determine if the given {@link BroadcastRecord} is eligible to launch processes.
+     */
+    public boolean disallowBackgroundStart(@NonNull BroadcastRecord r) {
+        return ((r.intent.getFlags() & Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
+                || (r.intent.getComponent() == null
+                        && r.intent.getPackage() == null
+                        && ((r.intent.getFlags()
+                                        & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
+                        && !isSignaturePerm(r.requiredPermissions));
+    }
+
+    /**
      * Determine if the given {@link BroadcastRecord} is eligible to be sent to
      * the given {@link BroadcastFilter}.
      *
@@ -624,7 +631,7 @@
     /**
      * Return true if all given permissions are signature-only perms.
      */
-    private boolean isSignaturePerm(String[] perms) {
+    private static boolean isSignaturePerm(String[] perms) {
         if (perms == null) {
             return false;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index cb409fe..0248010 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -125,7 +125,7 @@
             return proto.getBytes();
         }
 
-        @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_BIOMETRIC)
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
         public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(
                 String opPackageName) {
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java
index 39b1354..c9ae735 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java
@@ -86,12 +86,8 @@
     }
 
     @VisibleForTesting
-    boolean programInfosAreExactly(RadioManager.ProgramInfo... programInfos) {
-        Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> expectedMap = new ArrayMap<>();
-        for (int i = 0; i < programInfos.length; i++) {
-            expectedMap.put(programInfos[i].getSelector().getPrimaryId(), programInfos[i]);
-        }
-        return expectedMap.equals(mProgramInfoMap);
+    List<RadioManager.ProgramInfo> toProgramInfoList() {
+        return new ArrayList<>(mProgramInfoMap.values());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 5ff1909b..ac8ff21 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -32,6 +32,7 @@
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_DELETED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_REPLACED;
 import static com.android.server.pm.AppsFilterUtils.canQueryAsInstaller;
+import static com.android.server.pm.AppsFilterUtils.canQueryAsUpdateOwner;
 import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents;
 import static com.android.server.pm.AppsFilterUtils.canQueryViaPackage;
 import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary;
@@ -670,7 +671,8 @@
                     }
                 }
                 if (canQueryViaPackage(existingPkg, newPkg)
-                        || canQueryAsInstaller(existingSetting, newPkg)) {
+                        || canQueryAsInstaller(existingSetting, newPkg)
+                        || canQueryAsUpdateOwner(existingSetting, newPkg)) {
                     synchronized (mQueriesViaPackageLock) {
                         mQueriesViaPackage.add(existingSetting.getAppId(),
                                 newPkgSetting.getAppId());
@@ -697,7 +699,8 @@
                     }
                 }
                 if (canQueryViaPackage(newPkg, existingPkg)
-                        || canQueryAsInstaller(newPkgSetting, existingPkg)) {
+                        || canQueryAsInstaller(newPkgSetting, existingPkg)
+                        || canQueryAsUpdateOwner(newPkgSetting, existingPkg)) {
                     synchronized (mQueriesViaPackageLock) {
                         mQueriesViaPackage.add(newPkgSetting.getAppId(),
                                 existingSetting.getAppId());
diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java
index 8da1d21..d38b83f 100644
--- a/services/core/java/com/android/server/pm/AppsFilterUtils.java
+++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java
@@ -91,6 +91,15 @@
         return false;
     }
 
+    public static boolean canQueryAsUpdateOwner(PackageStateInternal querying,
+            AndroidPackage potentialTarget) {
+        final InstallSource installSource = querying.getInstallSource();
+        if (potentialTarget.getPackageName().equals(installSource.mUpdateOwnerPackageName)) {
+            return true;
+        }
+        return false;
+    }
+
     public static boolean canQueryViaUsesLibrary(AndroidPackage querying,
             AndroidPackage potentialTarget) {
         if (potentialTarget.getLibraryNames().isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8ea7788..350f5ef 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1842,6 +1842,60 @@
         dispatchSessionSealed();
     }
 
+    @Override
+    public void seal() {
+        assertNotChild("seal");
+        assertCallerIsOwnerOrRoot();
+        try {
+            sealInternal();
+            for (var child : getChildSessions()) {
+                child.sealInternal();
+            }
+        } catch (PackageManagerException e) {
+            throw new IllegalStateException("Package is not valid", e);
+        }
+    }
+
+    private void sealInternal() throws PackageManagerException {
+        synchronized (mLock) {
+            sealLocked();
+        }
+    }
+
+    @Override
+    public List<String> fetchPackageNames() {
+        assertNotChild("fetchPackageNames");
+        assertCallerIsOwnerOrRoot();
+        var sessions = getSelfOrChildSessions();
+        var result = new ArrayList<String>(sessions.size());
+        for (var s : sessions) {
+            result.add(s.fetchPackageName());
+        }
+        return result;
+    }
+
+    private String fetchPackageName() {
+        assertSealed("fetchPackageName");
+        synchronized (mLock) {
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final List<File> addedFiles = getAddedApksLocked();
+            for (File addedFile : addedFiles) {
+                final ParseResult<ApkLite> result =
+                        ApkLiteParseUtils.parseApkLite(input.reset(), addedFile, 0);
+                if (result.isError()) {
+                    throw new IllegalStateException(
+                            "Can't parse package for session=" + sessionId, result.getException());
+                }
+                final ApkLite apk = result.getResult();
+                var packageName = apk.getPackageName();
+                if (packageName != null) {
+                    return packageName;
+                }
+            }
+            throw new IllegalStateException("Can't fetch package name for session=" + sessionId);
+        }
+    }
+
     /**
      * Kicks off the install flow. The first step is to persist 'sealed' flags
      * to prevent mutations of hard links created later.
@@ -2095,6 +2149,11 @@
         }
     }
 
+    @NonNull
+    private List<PackageInstallerSession> getSelfOrChildSessions() {
+        return isMultiPackage() ? getChildSessions() : Collections.singletonList(this);
+    }
+
     /**
      * Seal the session to prevent further modification.
      *
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a72ae56..0de1a4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -33,6 +33,7 @@
 import static com.android.server.pm.PackageManagerService.STUB_SUFFIX;
 import static com.android.server.pm.PackageManagerService.TAG;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -113,6 +114,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.file.Path;
 import java.security.SecureRandom;
 import java.security.cert.CertificateEncodingException;
@@ -148,6 +151,29 @@
             ThreadLocal.withInitial(() -> false);
 
     /**
+     * Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
+     * when the package attempting to join the sharedUserId is a new install.
+     */
+    public static final int SHARED_USER_ID_JOIN_TYPE_INSTALL = 0;
+    /**
+     * Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
+     * when the package attempting to join the sharedUserId is an update.
+     */
+    public static final int SHARED_USER_ID_JOIN_TYPE_UPDATE = 1;
+    /**
+     * Type used with {@link #canJoinSharedUserId(String, SigningDetails, SharedUserSetting, int)}
+     * when the package attempting to join the sharedUserId is a part of the system image.
+     */
+    public static final int SHARED_USER_ID_JOIN_TYPE_SYSTEM = 2;
+    @IntDef(prefix = { "TYPE_" }, value = {
+            SHARED_USER_ID_JOIN_TYPE_INSTALL,
+            SHARED_USER_ID_JOIN_TYPE_UPDATE,
+            SHARED_USER_ID_JOIN_TYPE_SYSTEM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SharedUserIdJoinType {}
+
+    /**
      * Components of apps targeting Android T and above will stop receiving intents from
      * external callers that do not match its declared intent filters.
      *
@@ -575,17 +601,9 @@
             // the older ones.  We check to see if either the new package is signed by an older cert
             // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok
             // with being sharedUser with the existing signing cert.
-            boolean match = canJoinSharedUserId(parsedSignatures,
-                    sharedUserSetting.getSigningDetails());
-            // Special case: if the sharedUserId capability check failed it could be due to this
-            // being the only package in the sharedUserId so far and the lineage being updated to
-            // deny the sharedUserId capability of the previous key in the lineage.
-            final ArraySet<PackageStateInternal> susPackageStates =
-                    (ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates();
-            if (!match && susPackageStates.size() == 1
-                    && susPackageStates.valueAt(0).getPackageName().equals(packageName)) {
-                match = true;
-            }
+            boolean match = canJoinSharedUserId(packageName, parsedSignatures, sharedUserSetting,
+                    pkgSetting.getSigningDetails().getSignatures() != null
+                            ? SHARED_USER_ID_JOIN_TYPE_UPDATE : SHARED_USER_ID_JOIN_TYPE_INSTALL);
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(
                         packageName, sharedUserSetting.signatures, parsedSignatures);
@@ -608,36 +626,6 @@
                         + " has no signatures that match those in shared user "
                         + sharedUserSetting.name + "; ignoring!");
             }
-            // It is possible that this package contains a lineage that blocks sharedUserId access
-            // to an already installed package in the sharedUserId signed with a previous key.
-            // Iterate over all of the packages in the sharedUserId and ensure any that are signed
-            // with a key in this package's lineage have the SHARED_USER_ID capability granted.
-            if (parsedSignatures.hasPastSigningCertificates()) {
-                for (int i = 0; i < susPackageStates.size(); i++) {
-                    PackageStateInternal shUidPkgSetting = susPackageStates.valueAt(i);
-                    // if the current package in the sharedUserId is the package being updated then
-                    // skip this check as the update may revoke the sharedUserId capability from
-                    // the key with which this app was previously signed.
-                    if (packageName.equals(shUidPkgSetting.getPackageName())) {
-                        continue;
-                    }
-                    SigningDetails shUidSigningDetails =
-                            shUidPkgSetting.getSigningDetails();
-                    // The capability check only needs to be performed against the package if it is
-                    // signed with a key that is in the lineage of the package being installed.
-                    if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
-                        if (!parsedSignatures.checkCapability(shUidSigningDetails,
-                                SigningDetails.CertCapabilities.SHARED_USER_ID)) {
-                            throw new PackageManagerException(
-                                    INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
-                                    "Package " + packageName
-                                            + " revoked the sharedUserId capability from the"
-                                            + " signing key used to sign "
-                                            + shUidPkgSetting.getPackageName());
-                        }
-                    }
-                }
-            }
             // If the lineage of this package diverges from the lineage of the sharedUserId then
             // do not allow the installation to proceed.
             if (!parsedSignatures.hasCommonAncestor(
@@ -651,25 +639,97 @@
     }
 
     /**
-     * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId
-     * with {@code sharedUserSigningDetails}.
+     * Returns whether the package {@code packageName} can join the sharedUserId based on the
+     * settings in {@code sharedUserSetting}.
      * <p>
      * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and
      * capabilities for each package in the sharedUserId. A package can join the sharedUserId if
      * its current signer is the same as the shared signer, or if the current signer of either
      * is in the signing lineage of the other with the {@link
      * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer
-     * in the lineage.
+     * in the lineage. In the case of a key compromise, an app signed with a lineage revoking
+     * this capability from a previous signing key can still join the sharedUserId with another
+     * app signed with this previous key if the joining app is being updated; however, a new
+     * install will not be allowed until all apps have rotated off the key with the capability
+     * revoked.
      *
+     * @param packageName           the name of the package seeking to join the sharedUserId
      * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the
-     *                             sharedUserId
-     * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId
+     *                              sharedUserId
+     * @param sharedUserSetting     the {@code SharedUserSetting} for the sharedUserId {@code
+     *                              packageName} is seeking to join
+     * @param joinType              the type of join (install, update, system, etc)
      * @return true if the package seeking to join the sharedUserId meets the requirements
      */
-    public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails,
-            @NonNull SigningDetails sharedUserSigningDetails) {
-        return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID)
-                || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID);
+    public static boolean canJoinSharedUserId(@NonNull String packageName,
+            @NonNull SigningDetails packageSigningDetails,
+            @NonNull SharedUserSetting sharedUserSetting, @SharedUserIdJoinType int joinType) {
+        SigningDetails sharedUserSigningDetails = sharedUserSetting.getSigningDetails();
+        boolean capabilityGranted =
+                packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID)
+                        || sharedUserSigningDetails.checkCapability(packageSigningDetails,
+                        SHARED_USER_ID);
+
+        // If the current signer for either the package or the sharedUserId is the current signer
+        // of the other or in the lineage of the other with the SHARED_USER_ID capability granted,
+        // then a system and update join type can proceed; an install join type is not allowed here
+        // since the sharedUserId may contain packages that are signed with a key untrusted by
+        // the new package.
+        if (capabilityGranted && joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL) {
+            return true;
+        }
+
+        // If the package is signed with a key that is no longer trusted by the sharedUserId, then
+        // the join should not be allowed unless this is a system join type; system packages can
+        // join the sharedUserId as long as they share a common lineage.
+        if (!capabilityGranted && sharedUserSigningDetails.hasAncestor(packageSigningDetails)) {
+            if (joinType == SHARED_USER_ID_JOIN_TYPE_SYSTEM) {
+                return true;
+            }
+            return false;
+        }
+
+        // If the package is signed with a rotated key that no longer trusts the sharedUserId key,
+        // then allow system and update join types to rotate away from an untrusted key; install
+        // join types are not allowed since a new package that doesn't trust a previous key
+        // shouldn't be allowed to join until all packages in the sharedUserId have rotated off the
+        // untrusted key.
+        if (!capabilityGranted && packageSigningDetails.hasAncestor(sharedUserSigningDetails)) {
+            if (joinType != SHARED_USER_ID_JOIN_TYPE_INSTALL) {
+                return true;
+            }
+            return false;
+        }
+
+        // If the capability is not granted and the package signatures are not an ancestor
+        // or descendant of the sharedUserId signatures, then do not allow any join type to join
+        // the sharedUserId since there are no common signatures.
+        if (!capabilityGranted) {
+            return false;
+        }
+
+        // At this point this is a new install with the capability granted; ensure the current
+        // packages in the sharedUserId are all signed by a key trusted by the new package.
+        final ArraySet<PackageStateInternal> susPackageStates =
+                (ArraySet<PackageStateInternal>) sharedUserSetting.getPackageStates();
+        if (packageSigningDetails.hasPastSigningCertificates()) {
+            for (PackageStateInternal shUidPkgSetting : susPackageStates) {
+                SigningDetails shUidSigningDetails = shUidPkgSetting.getSigningDetails();
+                // The capability check only needs to be performed against the package if it is
+                // signed with a key that is in the lineage of the package being installed.
+                if (packageSigningDetails.hasAncestor(shUidSigningDetails)) {
+                    if (!packageSigningDetails.checkCapability(shUidSigningDetails,
+                            SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+                        Slog.d(TAG, "Package " + packageName
+                                + " revoked the sharedUserId capability from the"
+                                + " signing key used to sign "
+                                + shUidPkgSetting.getPackageName());
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 99bcbc9..58dcb02 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -213,8 +213,9 @@
                     if (sharedUserSetting != null) {
                         if (sharedUserSetting.signaturesChanged != null
                                 && !PackageManagerServiceUtils.canJoinSharedUserId(
-                                parsedPackage.getSigningDetails(),
-                                sharedUserSetting.getSigningDetails())) {
+                                parsedPackage.getPackageName(), parsedPackage.getSigningDetails(),
+                                sharedUserSetting,
+                                PackageManagerServiceUtils.SHARED_USER_ID_JOIN_TYPE_SYSTEM)) {
                             if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
                                 // Mismatched signatures is an error and silently skipping system
                                 // packages will likely break the device in unforeseen ways.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a966d98..a8cf8cb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1829,23 +1829,6 @@
     }
 
     /**
-     * Gets the current user id, or the target user id in case there is a started user switch.
-     *
-     * @return id of current or target foreground user, or {@link UserHandle#USER_NULL} if
-     * {@link ActivityManagerInternal} is not available yet.
-     */
-    @VisibleForTesting
-    int getCurrentOrTargetUserId() {
-        ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
-        if (activityManagerInternal == null) {
-            Slog.w(LOG_TAG, "getCurrentOrTargetUserId() called too early, ActivityManagerInternal"
-                    + " is not set yet");
-            return UserHandle.USER_NULL;
-        }
-        return activityManagerInternal.getCurrentUser().id;
-    }
-
-    /**
      * Gets whether the user is the current foreground user or a started profile of that user.
      *
      * <p>Doesn't perform any permission check.
@@ -5419,7 +5402,8 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             final UserData userData;
-            if (userId == getCurrentOrTargetUserId()) {
+            int currentUser = getCurrentUserId();
+            if (currentUser == userId) {
                 Slog.w(LOG_TAG, "Current user cannot be removed.");
                 return false;
             }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 27d74d5..214fd61 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -149,7 +149,8 @@
             UserManager.DISALLOW_WIFI_DIRECT,
             UserManager.DISALLOW_ADD_WIFI_CONFIG,
             UserManager.DISALLOW_CELLULAR_2G,
-            UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO
+            UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
+            UserManager.DISALLOW_CONFIG_DEFAULT_APPS
     });
 
     public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e7159db..31f291f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -248,6 +248,9 @@
     private static final String MAX_NUM_COMPONENTS_ERR_MSG =
             "Total number of components has exceeded the maximum number: " + MAX_NUM_COMPONENTS;
 
+    /** The maximum permission name length. */
+    private static final int MAX_PERMISSION_NAME_LENGTH = 512;
+
     @IntDef(flag = true, prefix = { "PARSE_" }, value = {
             PARSE_CHATTY,
             PARSE_COLLECT_CERTIFICATES,
@@ -1263,6 +1266,11 @@
             // that may change.
             String name = sa.getNonResourceString(
                     R.styleable.AndroidManifestUsesPermission_name);
+            if (TextUtils.length(name) > MAX_PERMISSION_NAME_LENGTH) {
+                return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "The name in the <uses-permission> is greater than "
+                                + MAX_PERMISSION_NAME_LENGTH);
+            }
 
             int minSdkVersion =  parseMinOrMaxSdkVersion(sa,
                     R.styleable.AndroidManifestUsesPermission_minSdkVersion,
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
index 868f34b..0e92709 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningRegistration.java
@@ -21,10 +21,12 @@
 import android.os.OutcomeReceiver;
 import android.security.rkp.IGetKeyCallback;
 import android.security.rkp.IRegistration;
+import android.security.rkp.IStoreUpgradedKeyCallback;
 import android.security.rkp.service.RegistrationProxy;
 import android.security.rkp.service.RemotelyProvisionedKey;
 import android.util.Log;
 
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
@@ -36,8 +38,10 @@
  */
 final class RemoteProvisioningRegistration extends IRegistration.Stub {
     static final String TAG = RemoteProvisioningService.TAG;
-    private final ConcurrentHashMap<IGetKeyCallback, CancellationSignal> mOperations =
+    private final ConcurrentHashMap<IGetKeyCallback, CancellationSignal> mGetKeyOperations =
             new ConcurrentHashMap<>();
+    private final Set<IStoreUpgradedKeyCallback> mStoreUpgradedKeyOperations =
+            ConcurrentHashMap.newKeySet();
     private final RegistrationProxy mRegistration;
     private final Executor mExecutor;
 
@@ -49,7 +53,7 @@
 
         @Override
         public void onResult(RemotelyProvisionedKey result) {
-            mOperations.remove(mCallback);
+            mGetKeyOperations.remove(mCallback);
             Log.i(TAG, "Successfully fetched key for client " + mCallback.hashCode());
             android.security.rkp.RemotelyProvisionedKey parcelable =
                     new android.security.rkp.RemotelyProvisionedKey();
@@ -60,7 +64,7 @@
 
         @Override
         public void onError(Exception e) {
-            mOperations.remove(mCallback);
+            mGetKeyOperations.remove(mCallback);
             if (e instanceof OperationCanceledException) {
                 Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
                 wrapCallback(mCallback::onCancel);
@@ -79,7 +83,7 @@
     @Override
     public void getKey(int keyId, IGetKeyCallback callback) {
         CancellationSignal cancellationSignal = new CancellationSignal();
-        if (mOperations.putIfAbsent(callback, cancellationSignal) != null) {
+        if (mGetKeyOperations.putIfAbsent(callback, cancellationSignal) != null) {
             Log.e(TAG, "Client can only request one call at a time " + callback.hashCode());
             throw new IllegalArgumentException(
                     "Callback is already associated with an existing operation: "
@@ -92,14 +96,14 @@
                     new GetKeyReceiver(callback));
         } catch (Exception e) {
             Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
-            mOperations.remove(callback);
+            mGetKeyOperations.remove(callback);
             wrapCallback(() -> callback.onError(e.getMessage()));
         }
     }
 
     @Override
     public void cancelGetKey(IGetKeyCallback callback) {
-        CancellationSignal cancellationSignal = mOperations.remove(callback);
+        CancellationSignal cancellationSignal = mGetKeyOperations.remove(callback);
         if (cancellationSignal == null) {
             throw new IllegalArgumentException(
                     "Invalid client in cancelGetKey: " + callback.hashCode());
@@ -110,9 +114,35 @@
     }
 
     @Override
-    public void storeUpgradedKey(byte[] oldKeyBlob, byte[] newKeyBlob) {
-        // TODO(b/262748535)
-        Log.e(TAG, "RegistrationBinder.storeUpgradedKey NOT YET IMPLEMENTED");
+    public void storeUpgradedKeyAsync(byte[] oldKeyBlob, byte[] newKeyBlob,
+            IStoreUpgradedKeyCallback callback) {
+        if (!mStoreUpgradedKeyOperations.add(callback)) {
+            throw new IllegalArgumentException(
+                    "Callback is already associated with an existing operation: "
+                            + callback.hashCode());
+        }
+
+        try {
+            mRegistration.storeUpgradedKeyAsync(oldKeyBlob, newKeyBlob, mExecutor,
+                    new OutcomeReceiver<>() {
+                        @Override
+                        public void onResult(Void result) {
+                            mStoreUpgradedKeyOperations.remove(callback);
+                            wrapCallback(callback::onSuccess);
+                        }
+
+                        @Override
+                        public void onError(Exception e) {
+                            mStoreUpgradedKeyOperations.remove(callback);
+                            wrapCallback(() -> callback.onError(e.getMessage()));
+                        }
+                    });
+        } catch (Exception e) {
+            Log.e(TAG, "storeUpgradedKeyAsync threw an exception for client "
+                    + callback.hashCode(), e);
+            mStoreUpgradedKeyOperations.remove(callback);
+            wrapCallback(() -> callback.onError(e.getMessage()));
+        }
     }
 
     interface CallbackRunner {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 56cd7a9..7909ba4 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -283,7 +283,7 @@
         assertFalse(
                 appsFilter.shouldFilterApplication(mSnapshot, DUMMY_CALLING_APPID, calling, target,
                         SYSTEM_USER));
-        watcher.verifyNoChangeReported("shouldFilterAplication");
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -1081,7 +1081,7 @@
         assertTrue(
                 appsFilter.shouldFilterApplication(mSnapshot, DUMMY_CALLING_APPID, calling, target,
                         SYSTEM_USER));
-        watcher.verifyNoChangeReported("shouldFilterAplication");
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -1110,7 +1110,36 @@
         assertFalse(
                 appsFilter.shouldFilterApplication(mSnapshot, DUMMY_CALLING_APPID, calling, target,
                         SYSTEM_USER));
-        watcher.verifyNoChangeReported("shouldFilterAplication");
+        watcher.verifyNoChangeReported("shouldFilterApplication");
+    }
+
+    @Test
+    public void testUpdateOwner_DoesntFilter() throws Exception {
+        final AppsFilterImpl appsFilter =
+                new AppsFilterImpl(mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */
+                        false, /* overlayProvider */ null, mMockHandler);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
+        simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
+        appsFilter.onSystemReady(mPmInternal);
+        watcher.verifyChangeReported("systemReady");
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
+                DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("add package");
+        PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
+                DUMMY_CALLING_APPID, withInstallSource(null /* initiatingPackageName */,
+                        null /* originatingPackageName */, null /* installerPackageName */,
+                        INVALID_UID, target.getPackageName(),
+                        null /* installerAttributionTag */,
+                        false /* isInitiatingPackageUninstalled */));
+        watcher.verifyChangeReported("add package");
+
+        assertFalse(
+                appsFilter.shouldFilterApplication(mSnapshot, DUMMY_CALLING_APPID, calling, target,
+                        SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -1139,7 +1168,7 @@
         assertFalse(
                 appsFilter.shouldFilterApplication(mSnapshot, DUMMY_TARGET_APPID, target,
                         instrumentation, SYSTEM_USER));
-        watcher.verifyNoChangeReported("shouldFilterAplication");
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
index 470f2be..7b361d3 100644
--- a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
@@ -34,7 +34,9 @@
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.OutcomeReceiver;
+import android.os.RemoteException;
 import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.IStoreUpgradedKeyCallback;
 import android.security.rkp.service.RegistrationProxy;
 import android.security.rkp.service.RemotelyProvisionedKey;
 
@@ -72,6 +74,12 @@
         return answerVoid(answer);
     }
 
+    // answerVoid wrapper for mocking storeUpgradeKeyAsync.
+    static Answer<Void> answerStoreUpgradedKeyAsync(
+            VoidAnswer4<byte[], byte[], Executor, OutcomeReceiver<Void, Exception>> answer) {
+        return answerVoid(answer);
+    }
+
     // matcher helper, making it easier to match the different key types
     private android.security.rkp.RemotelyProvisionedKey matches(
             RemotelyProvisionedKey expectedKey) {
@@ -178,16 +186,63 @@
 
     @Test
     public void storeUpgradedKeySuccess() throws Exception {
-        // TODO(b/262748535)
+        doAnswer(
+                answerStoreUpgradedKeyAsync((oldBlob, newBlob, executor, receiver) ->
+                        executor.execute(() -> receiver.onResult(null))))
+                .when(mRegistrationProxy)
+                .storeUpgradedKeyAsync(any(), any(), any(), any());
+
+        IStoreUpgradedKeyCallback callback = mock(IStoreUpgradedKeyCallback.class);
+        mRegistration.storeUpgradedKeyAsync(new byte[0], new byte[0], callback);
+        verify(callback).onSuccess();
+        verifyNoMoreInteractions(callback);
     }
 
     @Test
     public void storeUpgradedKeyFails() throws Exception {
-        // TODO(b/262748535)
+        final String errorString = "this is a failure";
+        doAnswer(
+                answerStoreUpgradedKeyAsync((oldBlob, newBlob, executor, receiver) ->
+                        executor.execute(() -> receiver.onError(new RemoteException(errorString)))))
+                .when(mRegistrationProxy)
+                .storeUpgradedKeyAsync(any(), any(), any(), any());
+
+        IStoreUpgradedKeyCallback callback = mock(IStoreUpgradedKeyCallback.class);
+        mRegistration.storeUpgradedKeyAsync(new byte[0], new byte[0], callback);
+        verify(callback).onError(errorString);
+        verifyNoMoreInteractions(callback);
     }
 
     @Test
-    public void storeUpgradedCatchesExceptionFromProxy() throws Exception {
-        // TODO(b/262748535)
+    public void storeUpgradedKeyHandlesException() throws Exception {
+        final String errorString = "all aboard the failboat, toot toot";
+        doThrow(new IllegalArgumentException(errorString))
+                .when(mRegistrationProxy)
+                .storeUpgradedKeyAsync(any(), any(), any(), any());
+
+        IStoreUpgradedKeyCallback callback = mock(IStoreUpgradedKeyCallback.class);
+        mRegistration.storeUpgradedKeyAsync(new byte[0], new byte[0], callback);
+        verify(callback).onError(errorString);
+        verifyNoMoreInteractions(callback);
     }
+
+    @Test
+    public void storeUpgradedKeyDuplicateCallback() throws Exception {
+        IStoreUpgradedKeyCallback callback = mock(IStoreUpgradedKeyCallback.class);
+
+        doAnswer(
+                answerStoreUpgradedKeyAsync((oldBlob, newBlob, executor, receiver) -> {
+                    assertThrows(IllegalArgumentException.class,
+                            () -> mRegistration.storeUpgradedKeyAsync(new byte[0], new byte[0],
+                                    callback));
+                    executor.execute(() -> receiver.onResult(null));
+                }))
+                .when(mRegistrationProxy)
+                .storeUpgradedKeyAsync(any(), any(), any(), any());
+
+        mRegistration.storeUpgradedKeyAsync(new byte[0], new byte[0], callback);
+        verify(callback).onSuccess();
+        verifyNoMoreInteractions(callback);
+    }
+
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 77127c5..92570aa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -22,7 +22,6 @@
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_MANIFEST;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ORDERED;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
-import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_RESULT_TO;
 import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
 import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
 import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
@@ -115,8 +114,29 @@
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
         mConstants.DELAY_CACHED_MILLIS = 120_000;
 
+        final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
+            public boolean shouldSkip(BroadcastRecord r, Object o) {
+                // Ignored
+                return false;
+            }
+            public String shouldSkipMessage(BroadcastRecord r, Object o) {
+                // Ignored
+                return null;
+            }
+            public boolean disallowBackgroundStart(BroadcastRecord r) {
+                // Ignored
+                return false;
+            }
+        };
+        final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
+            public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+                // Ignored
+            }
+        };
+
+
         mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
-                mConstants, mConstants);
+            mConstants, mConstants, emptySkipPolicy, emptyHistory);
 
         doReturn(1L).when(mQueue1).getRunnableAt();
         doReturn(2L).when(mQueue2).getRunnableAt();
@@ -200,7 +220,8 @@
 
     private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
             BroadcastRecord record, int recordIndex, long enqueueTime) {
-        queue.enqueueOrReplaceBroadcast(record, recordIndex, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(record, recordIndex,
+                null /* replacedBroadcastConsumer */, false);
         record.enqueueTime = enqueueTime;
     }
 
@@ -330,7 +351,8 @@
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
                 List.of(makeMockRegisteredReceiver()));
-        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
 
         queue.setProcessCached(false);
         final long notCachedRunnableAt = queue.getRunnableAt();
@@ -352,12 +374,14 @@
         // enqueue a bg-priority broadcast then a fg-priority one
         final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
         final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone);
-        queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(timezoneRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
-        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
 
         // verify that:
         // (a) the queue is immediately runnable by existence of a fg-priority broadcast
@@ -388,7 +412,8 @@
         final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, null,
                 List.of(withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
                         withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 0)), true);
-        queue.enqueueOrReplaceBroadcast(airplaneRecord, 1, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(airplaneRecord, 1,
+                null /* replacedBroadcastConsumer */, false);
 
         assertFalse(queue.isRunnable());
         assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
@@ -411,7 +436,8 @@
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
                 List.of(makeMockRegisteredReceiver()));
-        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
 
         mConstants.MAX_PENDING_BROADCASTS = 128;
         queue.invalidateRunnableAt();
@@ -437,11 +463,13 @@
                 new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
                 List.of(makeMockRegisteredReceiver()));
 
-        queue.enqueueOrReplaceBroadcast(lazyRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(lazyRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
         assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime);
         assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason);
 
-        queue.enqueueOrReplaceBroadcast(testRecord, 0, null /* replacedBroadcastConsumer */);
+        queue.enqueueOrReplaceBroadcast(testRecord, 0,
+                null /* replacedBroadcastConsumer */, false);
         assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime);
         assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason);
     }
@@ -459,13 +487,6 @@
     }
 
     @Test
-    public void testRunnableAt_Cached_ResultTo() {
-        final IIntentReceiver resultTo = mock(IIntentReceiver.class);
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
-                List.of(makeMockRegisteredReceiver()), resultTo, false), REASON_CONTAINS_RESULT_TO);
-    }
-
-    @Test
     public void testRunnableAt_Cached_Foreground() {
         final Intent foregroundIntent = new Intent();
         foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -511,25 +532,25 @@
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
                         .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)
                         .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
         queue.enqueueOrReplaceBroadcast(
                 makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
-                null /* replacedBroadcastConsumer */);
+                null /* replacedBroadcastConsumer */, false);
 
         queue.makeActiveNextPending();
         assertEquals(Intent.ACTION_LOCKED_BOOT_COMPLETED, queue.getActive().intent.getAction());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 8d9dda08..64be0f7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -357,6 +357,11 @@
             }
             receiversToSkip.add(o);
         }
+        public boolean disallowBackgroundStart(BroadcastRecord r) {
+            // Ignored
+            return false;
+        }
+
     }
 
     private class TestInjector extends Injector {
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 574aaf0..5f55f09 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -97,6 +97,8 @@
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getCacheDir()).thenReturn(originalContext.getCacheDir());
+        when(mContext.getAttributionSource()).thenReturn(originalContext.getAttributionSource());
         when(mContext.getResources()).thenReturn(mResources);
         // To prevent NullPointerException at the constructor of ActivityManagerConstants.
         when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
index b00fb92..bbe8907 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
@@ -29,7 +29,6 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 import android.platform.test.annotations.Postsubmit;
-import android.provider.Settings;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -38,7 +37,6 @@
 
 import com.android.internal.util.FunctionalUtils;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -64,22 +62,12 @@
     private Context mContext;
     private UserManager mUserManager;
     private ActivityManager mActivityManager;
-    private String mRemoveGuestOnExitOriginalValue;
 
     @Before
     public void setup() {
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mUserManager = mContext.getSystemService(UserManager.class);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
-        mRemoveGuestOnExitOriginalValue = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.REMOVE_GUEST_ON_EXIT);
-
-    }
-
-    @After
-    public void tearDown() {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.REMOVE_GUEST_ON_EXIT, mRemoveGuestOnExitOriginalValue);
     }
 
     /**
@@ -117,9 +105,6 @@
      **/
     @Test
     public void switchToExistingGuestAndStartOverStressTest() throws Exception {
-        Settings.Global.putString(mContext.getContentResolver(),
-                Settings.Global.REMOVE_GUEST_ON_EXIT, "0");
-
         if (ActivityManager.getCurrentUser() != USER_SYSTEM) {
             switchUser(USER_SYSTEM);
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
index da3c62d..cb530da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.WindowInsets.Type.ime
 import android.view.WindowInsets.Type.navigationBars
@@ -64,16 +63,6 @@
         transitions { testApp.dismissDialog(wmHelper) }
     }
 
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
-
     /** Checks that [ComponentNameMatcher.IME] layer becomes visible during the transition */
     @Presubmit @Test fun imeWindowIsAlwaysVisible() = flicker.imeWindowIsAlwaysVisible()
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
index c2526d3..0e732b5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
@@ -68,25 +68,11 @@
         teardown { imeTestApp.exit(wmHelper) }
     }
 
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
-
-    @Postsubmit
-    @Test
-    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
-
     @Presubmit @Test fun imeWindowBecomesVisible() = flicker.imeWindowBecomesVisible()
 
     @Presubmit @Test fun imeLayerBecomesVisible() = flicker.imeLayerBecomesVisible()
 
-    @Postsubmit
+    @FlakyTest(bugId = 240918620)
     @Test
     fun snapshotStartingWindowLayerCoversExactlyOnApp() {
         Assume.assumeFalse(isShellTransitionsEnabled)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index a6bbf54..477ddb3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.ime
 
 import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
@@ -92,52 +91,10 @@
             wmHelper.StateSyncBuilder().withFullScreenApp(imeTestApp).waitForAndVerify()
         }
     }
-
     /** {@inheritDoc} */
-    @Postsubmit
+    @FlakyTest(bugId = 265016201)
     @Test
-    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
-    /** {@inheritDoc} */
-    @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun statusBarLayerIsVisibleAtStartAndEnd() =
-        super.statusBarLayerIsVisibleAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
-    /** {@inheritDoc} */
-    @Postsubmit
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 1b23952..8b250c3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
@@ -121,11 +120,6 @@
         rotationLayerAppearsAndVanishesAssertion()
     }
 
-    /** {@inheritDoc} */
-    @FlakyTest(bugId = 206753786)
-    @Test
-    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
     @Test
     @IwTest(focusArea = "framework")
     override fun cujCompleted() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index c26485b..d76c94d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.rotation
 
-import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.IwTest
 import android.platform.test.annotations.Presubmit
 import android.view.WindowManager
@@ -201,11 +200,6 @@
         flicker.assertEventLog { this.focusDoesNotChange() }
     }
 
-    /** {@inheritDoc} */
-    @FlakyTest
-    @Test
-    override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
-
     @Test
     @IwTest(focusArea = "framework")
     override fun cujCompleted() {
@@ -224,6 +218,7 @@
         runAndIgnoreAssumptionViolation { appLayerAlwaysVisible() }
         runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
         runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
+        runAndIgnoreAssumptionViolation { navBarLayerPositionAtStartAndEnd() }
         runAndIgnoreAssumptionViolation { taskBarLayerIsVisibleAtStartAndEnd() }
         runAndIgnoreAssumptionViolation { taskBarWindowIsAlwaysVisible() }
     }
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt
index 3d5d01c..0ef165f 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/Constants.kt
@@ -35,6 +35,6 @@
         Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission")
 )
 
-const val ANNOTATION_PERMISSION_METHOD = "android.content.pm.PermissionMethod"
-const val ANNOTATION_PERMISSION_NAME = "android.content.pm.PermissionName"
+const val ANNOTATION_PERMISSION_METHOD = "android.annotation.PermissionMethod"
+const val ANNOTATION_PERMISSION_NAME = "android.annotation.PermissionName"
 const val ANNOTATION_PERMISSION_RESULT = "android.content.pm.PackageManager.PermissionResult"