Merge "Reintroduce InputMethodInfo constructor for CTS" into main
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 8b8d361..a142450 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -134,8 +134,9 @@
     return env;
 }
 
-std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
-                                     uint16_t bus, const std::vector<uint8_t>& descriptor,
+std::unique_ptr<Device> Device::open(int32_t id, const char* name, const char* uniq, int32_t vid,
+                                     int32_t pid, uint16_t bus,
+                                     const std::vector<uint8_t>& descriptor,
                                      std::unique_ptr<DeviceCallback> callback) {
     size_t size = descriptor.size();
     if (size > HID_MAX_DESCRIPTOR_SIZE) {
@@ -152,8 +153,7 @@
     struct uhid_event ev = {};
     ev.type = UHID_CREATE2;
     strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
-    std::string uniq = android::base::StringPrintf("Id: %d", id);
-    strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
+    strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq, sizeof(ev.u.create2.uniq));
     memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
     ev.u.create2.rd_size = size;
     ev.u.create2.bus = bus;
@@ -314,19 +314,31 @@
     return data;
 }
 
-static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
-                        jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
+static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jstring rawUniq, jint id,
+                        jint vid, jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
     ScopedUtfChars name(env, rawName);
     if (name.c_str() == nullptr) {
         return 0;
     }
 
+    std::string uniq;
+    if (rawUniq != nullptr) {
+        uniq = ScopedUtfChars(env, rawUniq);
+    } else {
+        uniq = android::base::StringPrintf("Id: %d", id);
+    }
+
+    if (uniq.c_str() == nullptr) {
+        return 0;
+    }
+
     std::vector<uint8_t> desc = getData(env, rawDescriptor);
 
     std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
 
     std::unique_ptr<uhid::Device> d =
-            uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
+            uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()),
+                               reinterpret_cast<const char*>(uniq.c_str()), vid, pid, bus, desc,
                                std::move(cb));
     return reinterpret_cast<jlong>(d.release());
 }
@@ -370,7 +382,7 @@
 
 static JNINativeMethod sMethods[] = {
         {"nativeOpenDevice",
-         "(Ljava/lang/String;IIII[B"
+         "(Ljava/lang/String;Ljava/lang/String;IIII[B"
          "Lcom/android/commands/hid/Device$DeviceCallback;)J",
          reinterpret_cast<void*>(openDevice)},
         {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 9c6060d..bc7a909 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -42,8 +42,9 @@
 
 class Device {
 public:
-    static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid,
-                                        uint16_t bus, const std::vector<uint8_t>& descriptor,
+    static std::unique_ptr<Device> open(int32_t id, const char* name, const char* uniq, int32_t vid,
+                                        int32_t pid, uint16_t bus,
+                                        const std::vector<uint8_t>& descriptor,
                                         std::unique_ptr<DeviceCallback> callback);
 
     ~Device();
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 0415037..4e8adc3 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -71,6 +71,7 @@
 
     private static native long nativeOpenDevice(
             String name,
+            String uniq,
             int id,
             int vid,
             int pid,
@@ -89,6 +90,7 @@
     public Device(
             int id,
             String name,
+            String uniq,
             int vid,
             int pid,
             int bus,
@@ -113,8 +115,9 @@
         } else {
             args.arg1 = id + ":" + vid + ":" + pid;
         }
-        args.arg2 = descriptor;
-        args.arg3 = report;
+        args.arg2 = uniq;
+        args.arg3 = descriptor;
+        args.arg4 = report;
         mHandler.obtainMessage(MSG_OPEN_DEVICE, args).sendToTarget();
         mTimeToSend = SystemClock.uptimeMillis();
     }
@@ -167,11 +170,12 @@
                     mPtr =
                             nativeOpenDevice(
                                     (String) args.arg1,
+                                    (String) args.arg2,
                                     args.argi1,
                                     args.argi2,
                                     args.argi3,
                                     args.argi4,
-                                    (byte[]) args.arg2,
+                                    (byte[]) args.arg3,
                                     new DeviceCallback());
                     pauseEvents();
                     break;
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 3efb797..09ed528 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -56,6 +56,7 @@
     private int mId;
     private String mCommand;
     private String mName;
+    private String mUniq;
     private byte[] mDescriptor;
     private int mVid;
     private int mPid;
@@ -78,6 +79,10 @@
         return mName;
     }
 
+    public String getUniq() {
+        return mUniq;
+    }
+
     public byte[] getDescriptor() {
         return mDescriptor;
     }
@@ -118,6 +123,7 @@
         return "Event{id=" + mId
             + ", command=" + String.valueOf(mCommand)
             + ", name=" + String.valueOf(mName)
+            + ", uniq=" + String.valueOf(mUniq)
             + ", descriptor=" + Arrays.toString(mDescriptor)
             + ", vid=" + mVid
             + ", pid=" + mPid
@@ -149,6 +155,10 @@
             mEvent.mName = name;
         }
 
+        public void setUniq(String uniq) {
+            mEvent.mUniq = uniq;
+        }
+
         public void setDescriptor(byte[] descriptor) {
             mEvent.mDescriptor = descriptor;
         }
@@ -247,6 +257,9 @@
                             case "name":
                                 eb.setName(mReader.nextString());
                                 break;
+                            case "uniq":
+                                eb.setUniq(mReader.nextString());
+                                break;
                             case "vid":
                                 eb.setVid(readInt());
                                 break;
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 2db791fe..5ebfd95 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -117,8 +117,17 @@
                     "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
         }
         int id = e.getId();
-        Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
-                e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
+        Device d = new Device(
+                id,
+                e.getName(),
+                e.getUniq(),
+                e.getVendorId(),
+                e.getProductId(),
+                e.getBus(),
+                e.getDescriptor(),
+                e.getReport(),
+                e.getFeatureReports(),
+                e.getOutputs());
         mDevices.append(id, d);
     }
 
diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp
index be02710..494d2ae3 100644
--- a/cmds/telecom/Android.bp
+++ b/cmds/telecom/Android.bp
@@ -21,5 +21,8 @@
 java_binary {
     name: "telecom",
     wrapper: "telecom.sh",
-    srcs: ["**/*.java"],
+    srcs: [
+        ":telecom-shell-commands-src",
+        "**/*.java",
+    ],
 }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 1488e14cf..50af5a7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -17,30 +17,22 @@
 package com.android.commands.telecom;
 
 import android.app.ActivityThread;
-import android.content.ComponentName;
 import android.content.Context;
-import android.net.Uri;
-import android.os.IUserManager;
 import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.sysprop.TelephonyProperties;
-import android.telecom.Log;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 
-import com.android.internal.os.BaseCommand;
 import com.android.internal.telecom.ITelecomService;
+import com.android.server.telecom.TelecomShellCommand;
 
-import java.io.PrintStream;
-import java.util.Arrays;
-import java.util.stream.Collectors;
+import java.io.FileDescriptor;
 
-public final class Telecom extends BaseCommand {
+/**
+ * @deprecated Use {@code com.android.server.telecom.TelecomShellCommand} instead and execute the
+ * shell command using {@code adb shell cmd telecom...}. This is only here for backwards
+ * compatibility reasons.
+ */
+@Deprecated
+public final class Telecom {
 
     /**
      * Command-line entry point.
@@ -52,458 +44,11 @@
         // TODO: Do it in zygote and RuntimeInit. b/148897549
         ActivityThread.initializeMainlineModules();
 
-      (new Telecom()).run(args);
-    }
-    private static final String CALLING_PACKAGE = Telecom.class.getPackageName();
-    private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
-    private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
-    private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
-    private static final String COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT =
-            "set-user-selected-outgoing-phone-account";
-    private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
-    private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
-    private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
-    private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
-            "add-or-remove-call-companion-app";
-    private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
-            "set-phone-acct-suggestion-component";
-    private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
-    private static final String COMMAND_SET_CALL_DIAGNOSTIC_SERVICE = "set-call-diagnostic-service";
-    private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
-    private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
-    private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
-    private static final String COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls";
-    private static final String COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS =
-            "cleanup-orphan-phone-accounts";
-    private static final String COMMAND_RESET_CAR_MODE = "reset-car-mode";
-    private static final String COMMAND_IS_NON_IN_CALL_SERVICE_BOUND =
-            "is-non-ui-in-call-service-bound";
-
-    /**
-     * Change the system dialer package name if a package name was specified,
-     * Example: adb shell telecom set-system-dialer <PACKAGE>
-     *
-     * Restore it to the default if if argument is "default" or no argument is passed.
-     * Example: adb shell telecom set-system-dialer default
-     */
-    private static final String COMMAND_SET_SYSTEM_DIALER = "set-system-dialer";
-    private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer";
-    private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers";
-    private static final String COMMAND_SET_SIM_COUNT = "set-sim-count";
-    private static final String COMMAND_GET_SIM_CONFIG = "get-sim-config";
-    private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
-    private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
-            "set-test-emergency-phone-account-package-filter";
-    /**
-     * Command used to emit a distinct "mark" in the logs.
-     */
-    private static final String COMMAND_LOG_MARK = "log-mark";
-
-    private ComponentName mComponent;
-    private String mAccountId;
-    private ITelecomService mTelecomService;
-    private TelephonyManager mTelephonyManager;
-    private IUserManager mUserManager;
-
-    @Override
-    public void onShowUsage(PrintStream out) {
-        out.println("usage: telecom [subcommand] [options]\n"
-                + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
-                + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
-                + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
-                + "usage: telecom register-sim-phone-account [-e] <COMPONENT> <ID> <USER_SN>"
-                        + " <LABEL>: registers a PhoneAccount with CAPABILITY_SIM_SUBSCRIPTION"
-                        + " and optionally CAPABILITY_PLACE_EMERGENCY_CALLS if \"-e\" is provided\n"
-                + "usage: telecom set-user-selected-outgoing-phone-account [-e] <COMPONENT> <ID> "
-                + "<USER_SN>\n"
-                + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
-                + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
-                + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
-                + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
-                + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
-                + " <LABEL> <ADDRESS>\n"
-                + "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n"
-                + "usage: telecom set-call-diagnostic-service <PACKAGE>\n"
-                + "usage: telecom set-default-dialer <PACKAGE>\n"
-                + "usage: telecom get-default-dialer\n"
-                + "usage: telecom get-system-dialer\n"
-                + "usage: telecom wait-on-handlers\n"
-                + "usage: telecom set-sim-count <COUNT>\n"
-                + "usage: telecom get-sim-config\n"
-                + "usage: telecom get-max-phones\n"
-                + "usage: telecom stop-block-suppression: Stop suppressing the blocked number"
-                        + " provider after a call to emergency services.\n"
-                + "usage: telecom cleanup-stuck-calls: Clear any disconnected calls that have"
-                + " gotten wedged in Telecom.\n"
-                + "usage: telecom cleanup-orphan-phone-accounts: remove any phone accounts that"
-                + " no longer have a valid UserHandle or accounts that no longer belongs to an"
-                + " installed package.\n"
-                + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n"
-                + "\n"
-                + "telecom set-phone-account-enabled: Enables the given phone account, if it has"
-                        + " already been registered with Telecom.\n"
-                + "\n"
-                + "telecom set-phone-account-disabled: Disables the given phone account, if it"
-                        + " has already been registered with telecom.\n"
-                + "\n"
-                + "telecom set-call-diagnostic-service: overrides call diagnostic service.\n"
-                + "telecom set-default-dialer: Sets the override default dialer to the given"
-                        + " component; this will override whatever the dialer role is set to.\n"
-                + "\n"
-                + "telecom get-default-dialer: Displays the current default dialer.\n"
-                + "\n"
-                + "telecom get-system-dialer: Displays the current system dialer.\n"
-                + "telecom set-system-dialer: Set the override system dialer to the given"
-                        + " component. To remove the override, send \"default\"\n"
-                + "\n"
-                + "telecom wait-on-handlers: Wait until all handlers finish their work.\n"
-                + "\n"
-                + "telecom set-sim-count: Set num SIMs (2 for DSDS, 1 for single SIM."
-                        + " This may restart the device.\n"
-                + "\n"
-                + "telecom get-sim-config: Get the mSIM config string. \"DSDS\" for DSDS mode,"
-                        + " or \"\" for single SIM\n"
-                + "\n"
-                + "telecom get-max-phones: Get the max supported phones from the modem.\n"
-                + "telecom set-test-emergency-phone-account-package-filter <PACKAGE>: sets a"
-                        + " package name that will be used for test emergency calls. To clear,"
-                        + " send an empty package name. Real emergency calls will still be placed"
-                        + " over Telephony.\n"
-                + "telecom log-mark <MESSAGE>: emits a message into the telecom logs.  Useful for "
-                        + "testers to indicate where in the logs various test steps take place.\n"
-                + "telecom is-non-ui-in-call-service-bound <PACKAGE>: queries a particular "
-                + "non-ui-InCallService in InCallController to determine if it is bound \n"
-        );
-    }
-
-    @Override
-    public void onRun() throws Exception {
-        mTelecomService = ITelecomService.Stub.asInterface(
-                ServiceManager.getService(Context.TELECOM_SERVICE));
-        if (mTelecomService == null) {
-            Log.w(this, "onRun: Can't access telecom manager.");
-            showError("Error: Could not access the Telecom Manager. Is the system running?");
-            return;
-        }
-
         Looper.prepareMainLooper();
+        ITelecomService service = ITelecomService.Stub.asInterface(
+                ServiceManager.getService(Context.TELECOM_SERVICE));
         Context context = ActivityThread.systemMain().getSystemContext();
-        mTelephonyManager = context.getSystemService(TelephonyManager.class);
-        if (mTelephonyManager == null) {
-            Log.w(this, "onRun: Can't access telephony service.");
-            showError("Error: Could not access the Telephony Service. Is the system running?");
-            return;
-        }
-
-        mUserManager = IUserManager.Stub
-                .asInterface(ServiceManager.getService(Context.USER_SERVICE));
-        if (mUserManager == null) {
-            Log.w(this, "onRun: Can't access user manager.");
-            showError("Error: Could not access the User Manager. Is the system running?");
-            return;
-        }
-        Log.i(this, "onRun: parsing command.");
-        String command = nextArgRequired();
-        switch (command) {
-            case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
-                runSetPhoneAccountEnabled(true);
-                break;
-            case COMMAND_SET_PHONE_ACCOUNT_DISABLED:
-                runSetPhoneAccountEnabled(false);
-                break;
-            case COMMAND_REGISTER_PHONE_ACCOUNT:
-                runRegisterPhoneAccount();
-                break;
-            case COMMAND_SET_TEST_CALL_REDIRECTION_APP:
-                runSetTestCallRedirectionApp();
-                break;
-            case COMMAND_SET_TEST_CALL_SCREENING_APP:
-                runSetTestCallScreeningApp();
-                break;
-            case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
-                runAddOrRemoveCallCompanionApp();
-                break;
-            case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
-                runSetTestPhoneAcctSuggestionComponent();
-                break;
-            case COMMAND_SET_CALL_DIAGNOSTIC_SERVICE:
-                runSetCallDiagnosticService();
-                break;
-            case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
-                runRegisterSimPhoneAccount();
-                break;
-            case COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT:
-                runSetUserSelectedOutgoingPhoneAccount();
-                break;
-            case COMMAND_UNREGISTER_PHONE_ACCOUNT:
-                runUnregisterPhoneAccount();
-                break;
-            case COMMAND_STOP_BLOCK_SUPPRESSION:
-                runStopBlockSuppression();
-                break;
-            case COMMAND_CLEANUP_STUCK_CALLS:
-                runCleanupStuckCalls();
-                break;
-            case COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS:
-                runCleanupOrphanPhoneAccounts();
-                break;
-            case COMMAND_RESET_CAR_MODE:
-                runResetCarMode();
-                break;
-            case COMMAND_SET_DEFAULT_DIALER:
-                runSetDefaultDialer();
-                break;
-            case COMMAND_GET_DEFAULT_DIALER:
-                runGetDefaultDialer();
-                break;
-            case COMMAND_SET_SYSTEM_DIALER:
-                runSetSystemDialer();
-                break;
-            case COMMAND_GET_SYSTEM_DIALER:
-                runGetSystemDialer();
-                break;
-            case COMMAND_WAIT_ON_HANDLERS:
-                runWaitOnHandler();
-                break;
-            case COMMAND_SET_SIM_COUNT:
-                runSetSimCount();
-                break;
-            case COMMAND_GET_SIM_CONFIG:
-                runGetSimConfig();
-                break;
-            case COMMAND_GET_MAX_PHONES:
-                runGetMaxPhones();
-                break;
-            case COMMAND_IS_NON_IN_CALL_SERVICE_BOUND:
-                runIsNonUiInCallServiceBound();
-                break;
-            case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER:
-                runSetEmergencyPhoneAccountPackageFilter();
-                break;
-            case COMMAND_LOG_MARK:
-                runLogMark();
-                break;
-            default:
-                Log.w(this, "onRun: unknown command: %s", command);
-                throw new IllegalArgumentException ("unknown command '" + command + "'");
-        }
-    }
-
-    private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
-        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        final boolean success =  mTelecomService.enablePhoneAccount(handle, enabled);
-        if (success) {
-            System.out.println("Success - " + handle + (enabled ? " enabled." : " disabled."));
-        } else {
-            System.out.println("Error - is " + handle + " a valid PhoneAccount?");
-        }
-    }
-
-    private void runRegisterPhoneAccount() throws RemoteException {
-        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        final String label = nextArgRequired();
-        PhoneAccount account = PhoneAccount.builder(handle, label)
-                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
-        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
-        System.out.println("Success - " + handle + " registered.");
-    }
-
-    private void runRegisterSimPhoneAccount() throws RemoteException {
-        boolean isEmergencyAccount = false;
-        String opt;
-        while ((opt = nextOption()) != null) {
-            switch (opt) {
-                case "-e": {
-                    isEmergencyAccount = true;
-                    break;
-                }
-            }
-        }
-        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        final String label = nextArgRequired();
-        final String address = nextArgRequired();
-        int capabilities = PhoneAccount.CAPABILITY_CALL_PROVIDER
-                | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
-                | (isEmergencyAccount ? PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS : 0);
-        PhoneAccount account = PhoneAccount.builder(
-            handle, label)
-                .setAddress(Uri.parse(address))
-                .setSubscriptionAddress(Uri.parse(address))
-                .setCapabilities(capabilities)
-                .setShortDescription(label)
-                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
-                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
-                .build();
-        mTelecomService.registerPhoneAccount(account, CALLING_PACKAGE);
-        System.out.println("Success - " + handle + " registered.");
-    }
-
-    private void runSetTestCallRedirectionApp() throws RemoteException {
-        final String packageName = nextArg();
-        mTelecomService.setTestDefaultCallRedirectionApp(packageName);
-    }
-
-    private void runSetTestCallScreeningApp() throws RemoteException {
-        final String packageName = nextArg();
-        mTelecomService.setTestDefaultCallScreeningApp(packageName);
-    }
-
-    private void runAddOrRemoveCallCompanionApp() throws RemoteException {
-        final String packageName = nextArgRequired();
-        String isAdded = nextArgRequired();
-        boolean isAddedBool = "1".equals(isAdded);
-        mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
-    }
-
-    private void runSetCallDiagnosticService() throws RemoteException {
-        String packageName = nextArg();
-        if ("default".equals(packageName)) packageName = null;
-        mTelecomService.setTestCallDiagnosticService(packageName);
-        System.out.println("Success - " + packageName + " set as call diagnostic service.");
-    }
-
-    private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
-        final String componentName = nextArg();
-        mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
-    }
-
-    private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
-        Log.i(this, "runSetUserSelectedOutgoingPhoneAccount");
-        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        mTelecomService.setUserSelectedOutgoingPhoneAccount(handle);
-        System.out.println("Success - " + handle + " set as default outgoing account.");
-    }
-
-    private void runUnregisterPhoneAccount() throws RemoteException {
-        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
-        mTelecomService.unregisterPhoneAccount(handle, CALLING_PACKAGE);
-        System.out.println("Success - " + handle + " unregistered.");
-    }
-
-    private void runStopBlockSuppression() throws RemoteException {
-        mTelecomService.stopBlockSuppression();
-    }
-
-    private void runCleanupStuckCalls() throws RemoteException {
-        mTelecomService.cleanupStuckCalls();
-    }
-
-    private void runCleanupOrphanPhoneAccounts() throws RemoteException {
-        System.out.println("Success - cleaned up " + mTelecomService.cleanupOrphanPhoneAccounts()
-                + "  phone accounts.");
-    }
-
-    private void runResetCarMode() throws RemoteException {
-        mTelecomService.resetCarMode();
-    }
-
-    private void runSetDefaultDialer() throws RemoteException {
-        String packageName = nextArg();
-        if ("default".equals(packageName)) packageName = null;
-        mTelecomService.setTestDefaultDialer(packageName);
-        System.out.println("Success - " + packageName + " set as override default dialer.");
-    }
-
-    private void runSetSystemDialer() throws RemoteException {
-        final String flatComponentName = nextArg();
-        final ComponentName componentName = (flatComponentName.equals("default")
-                ? null : parseComponentName(flatComponentName));
-        mTelecomService.setSystemDialer(componentName);
-        System.out.println("Success - " + componentName + " set as override system dialer.");
-    }
-
-    private void runGetDefaultDialer() throws RemoteException {
-        System.out.println(mTelecomService.getDefaultDialerPackage(CALLING_PACKAGE));
-    }
-
-    private void runGetSystemDialer() throws RemoteException {
-        System.out.println(mTelecomService.getSystemDialerPackage(CALLING_PACKAGE));
-    }
-
-    private void runWaitOnHandler() throws RemoteException {
-
-    }
-
-    private void runSetSimCount() throws RemoteException {
-        if (!callerIsRoot()) {
-            System.out.println("set-sim-count requires adb root");
-            return;
-        }
-        int numSims = Integer.parseInt(nextArgRequired());
-        System.out.println("Setting sim count to " + numSims + ". Device may reboot");
-        mTelephonyManager.switchMultiSimConfig(numSims);
-    }
-
-    /**
-     * prints out whether a particular non-ui InCallServices is bound in a call
-     */
-    public void runIsNonUiInCallServiceBound() throws RemoteException {
-        if (TextUtils.isEmpty(mArgs.peekNextArg())) {
-            System.out.println("No Argument passed. Please pass a <PACKAGE_NAME> to lookup.");
-        } else {
-            System.out.println(
-                    String.valueOf(mTelecomService.isNonUiInCallServiceBound(nextArg())));
-        }
-    }
-
-    /**
-     * Prints the mSIM config to the console.
-     * "DSDS" for a phone in DSDS mode
-     * "" (empty string) for a phone in SS mode
-     */
-    private void runGetSimConfig() throws RemoteException {
-        System.out.println(TelephonyProperties.multi_sim_config().orElse(""));
-    }
-
-    private void runGetMaxPhones() throws RemoteException {
-        // how many logical modems can be potentially active simultaneously
-        System.out.println(mTelephonyManager.getSupportedModemCount());
-    }
-
-    private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException {
-        String packageName = mArgs.getNextArg();
-        if (TextUtils.isEmpty(packageName)) {
-            mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(null);
-            System.out.println("Success - filter cleared");
-        } else {
-            mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(packageName);
-            System.out.println("Success = filter set to " + packageName);
-        }
-
-    }
-
-    private void runLogMark() throws RemoteException {
-        String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" "));
-        mTelecomService.requestLogMark(message);
-    }
-
-    private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
-        if (TextUtils.isEmpty(mArgs.peekNextArg())) {
-            return null;
-        }
-        final ComponentName component = parseComponentName(nextArgRequired());
-        final String accountId = nextArgRequired();
-        final String userSnInStr = nextArgRequired();
-        UserHandle userHandle;
-        try {
-            final int userSn = Integer.parseInt(userSnInStr);
-            userHandle = UserHandle.of(mUserManager.getUserHandle(userSn));
-        } catch (NumberFormatException ex) {
-            Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
-            throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
-        }
-        return new PhoneAccountHandle(component, accountId, userHandle);
-    }
-
-    private boolean callerIsRoot() {
-        return Process.ROOT_UID == Process.myUid();
-    }
-
-    private ComponentName parseComponentName(String component) {
-        ComponentName cn = ComponentName.unflattenFromString(component);
-        if (cn == null) {
-            throw new IllegalArgumentException ("Invalid component " + component);
-        }
-        return cn;
+        new TelecomShellCommand(service, context).exec(null, FileDescriptor.in,
+                FileDescriptor.out, FileDescriptor.err, args);
     }
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index cd9c3ad..592cbfd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4445,7 +4445,7 @@
     method public final android.media.session.MediaController getMediaController();
     method @NonNull public android.view.MenuInflater getMenuInflater();
     method @NonNull public android.window.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
-    method public final android.app.Activity getParent();
+    method @Deprecated public final android.app.Activity getParent();
     method @Nullable public android.content.Intent getParentActivityIntent();
     method public android.content.SharedPreferences getPreferences(int);
     method @Nullable public android.net.Uri getReferrer();
@@ -4463,7 +4463,7 @@
     method public void invalidateOptionsMenu();
     method public boolean isActivityTransitionRunning();
     method public boolean isChangingConfigurations();
-    method public final boolean isChild();
+    method @Deprecated public final boolean isChild();
     method public boolean isDestroyed();
     method public boolean isFinishing();
     method public boolean isImmersive();
@@ -9608,7 +9608,7 @@
     method public void partiallyUpdateAppWidget(int, android.widget.RemoteViews);
     method @FlaggedApi("android.appwidget.flags.generated_previews") public void removeWidgetPreview(@NonNull android.content.ComponentName, int);
     method public boolean requestPinAppWidget(@NonNull android.content.ComponentName, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
-    method @FlaggedApi("android.appwidget.flags.generated_previews") public void setWidgetPreview(@NonNull android.content.ComponentName, int, @NonNull android.widget.RemoteViews);
+    method @FlaggedApi("android.appwidget.flags.generated_previews") public boolean setWidgetPreview(@NonNull android.content.ComponentName, int, @NonNull android.widget.RemoteViews);
     method public void updateAppWidget(int[], android.widget.RemoteViews);
     method public void updateAppWidget(int, android.widget.RemoteViews);
     method public void updateAppWidget(android.content.ComponentName, android.widget.RemoteViews);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ee69ce1..b73f199 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -403,7 +403,6 @@
     field @Deprecated public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
-    field @FlaggedApi("android.hardware.biometrics.face_background_authentication") public static final String USE_BACKGROUND_FACE_AUTHENTICATION = "android.permission.USE_BACKGROUND_FACE_AUTHENTICATION";
     field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
     field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String USE_ON_DEVICE_INTELLIGENCE = "android.permission.USE_ON_DEVICE_INTELLIGENCE";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
@@ -3761,7 +3760,6 @@
     field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
-    field @FlaggedApi("android.hardware.biometrics.face_background_authentication") public static final String FACE_SERVICE = "face";
     field public static final String FONT_SERVICE = "font";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
@@ -5139,15 +5137,6 @@
 
 }
 
-package android.hardware.face {
-
-  @FlaggedApi("android.hardware.biometrics.face_background_authentication") public class FaceManager {
-    method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION) public void authenticateInBackground(@Nullable java.util.concurrent.Executor, @Nullable android.hardware.biometrics.BiometricPrompt.CryptoObject, @Nullable android.os.CancellationSignal, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
-    method @FlaggedApi("android.hardware.biometrics.face_background_authentication") @RequiresPermission(anyOf={"android.permission.USE_BIOMETRIC_INTERNAL", android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION}) public boolean hasEnrolledTemplates();
-  }
-
-}
-
 package android.hardware.hdmi {
 
   public abstract class HdmiClient {
@@ -11539,7 +11528,7 @@
     method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
     method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForStartDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
     method public void finishDataDelivery(@NonNull String, @NonNull android.content.AttributionSource);
-    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public java.util.Map<java.lang.String,android.permission.PermissionManager.PermissionState> getAllPermissionStates(@NonNull String, @NonNull String);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public java.util.Map<java.lang.String,android.permission.PermissionManager.PermissionState> getAllPermissionStates(@NonNull String, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
     method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
     method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public int getPermissionFlags(@NonNull String, @NonNull String, @NonNull String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 63cafdc..44dc8e2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1254,12 +1254,23 @@
         return mApplication;
     }
 
-    /** Is this activity embedded inside of another activity? */
+    /**
+     * Whether this is a child {@link Activity} of an {@link ActivityGroup}.
+     *
+     * @deprecated {@link ActivityGroup} is deprecated.
+     */
+    @Deprecated
     public final boolean isChild() {
         return mParent != null;
     }
 
-    /** Return the parent activity if this view is an embedded child. */
+    /**
+     * Returns the parent {@link Activity} if this is a child {@link Activity} of an
+     * {@link ActivityGroup}.
+     *
+     * @deprecated {@link ActivityGroup} is deprecated.
+     */
+    @Deprecated
     public final Activity getParent() {
         return mParent;
     }
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fb1b17b..89199ca 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1,5 +1,7 @@
 package android.app.assist;
 
+import static android.credentials.Constants.FAILURE_CREDMAN_SELECTOR;
+import static android.credentials.Constants.SUCCESS_CREDMAN_SELECTOR;
 import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
 
 import android.annotation.FlaggedApi;
@@ -20,14 +22,17 @@
 import android.os.BadParcelableException;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.OutcomeReceiver;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PooledStringReader;
 import android.os.PooledStringWriter;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.service.autofill.FillRequest;
 import android.service.credentials.CredentialProviderService;
@@ -37,6 +42,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 import android.view.View;
 import android.view.View.AutofillImportance;
 import android.view.ViewRootImpl;
@@ -652,6 +658,9 @@
         @Nullable OutcomeReceiver<GetCredentialResponse, GetCredentialException>
                 mGetCredentialCallback;
 
+        @Nullable ResultReceiver mGetCredentialResultReceiver;
+
+
         AutofillValue mAutofillValue;
         CharSequence[] mAutofillOptions;
         boolean mSanitized;
@@ -916,6 +925,7 @@
                 mExtras = in.readBundle();
             }
             mGetCredentialRequest = in.readTypedObject(GetCredentialRequest.CREATOR);
+            mGetCredentialResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
         }
 
         /**
@@ -1153,6 +1163,7 @@
                 out.writeBundle(mExtras);
             }
             out.writeTypedObject(mGetCredentialRequest, flags);
+            out.writeTypedObject(mGetCredentialResultReceiver, flags);
             return flags;
         }
 
@@ -1295,9 +1306,8 @@
          */
         @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
         @Nullable
-        public OutcomeReceiver<GetCredentialResponse,
-                GetCredentialException> getCredentialManagerCallback() {
-            return mGetCredentialCallback;
+        public ResultReceiver getCredentialManagerCallback() {
+            return mGetCredentialResultReceiver;
         }
 
         /**
@@ -1894,6 +1904,7 @@
         final AssistStructure mAssist;
         final ViewNode mNode;
         final boolean mAsync;
+        private Handler mHandler;
 
         /**
          * Used to instantiate a builder for a stand-alone {@link ViewNode} which is not associated
@@ -2271,6 +2282,56 @@
                 option.getCandidateQueryData()
                         .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
             }
+            setUpResultReceiver(callback);
+        }
+
+        private void setUpResultReceiver(
+                OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
+
+            if (mHandler == null) {
+                mHandler = new Handler(Looper.getMainLooper(), null, true);
+            }
+            final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle resultData) {
+                    if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
+                        Slog.d(TAG, "onReceiveResult from Credential Manager");
+                        GetCredentialResponse getCredentialResponse =
+                                resultData.getParcelable(
+                                        CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+                                        GetCredentialResponse.class);
+
+                        callback.onResult(getCredentialResponse);
+                    } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
+                        String[] exception =  resultData.getStringArray(
+                                CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
+                        if (exception != null && exception.length >= 2) {
+                            Slog.w(TAG, "Credman bottom sheet from pinned "
+                                    + "entry failed with: + " + exception[0] + " , "
+                                    + exception[1]);
+                            callback.onError(new GetCredentialException(
+                                    exception[0], exception[1]));
+                        }
+                    } else {
+                        Slog.d(TAG, "Unknown resultCode from credential "
+                                + "manager bottom sheet: " + resultCode);
+                    }
+                }
+            };
+            ResultReceiver ipcFriendlyResultReceiver =
+                    toIpcFriendlyResultReceiver(resultReceiver);
+            mNode.mGetCredentialResultReceiver = ipcFriendlyResultReceiver;
+        }
+
+        private ResultReceiver toIpcFriendlyResultReceiver(ResultReceiver resultReceiver) {
+            final Parcel parcel = Parcel.obtain();
+            resultReceiver.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+
+            final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
+            parcel.recycle();
+
+            return ipcFriendly;
         }
 
         @Override
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index eb82e1f..cda4d89 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1417,13 +1417,15 @@
      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN
      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD
      * @see AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX
+     *
+     * @return true if the call was successful, false if it was rate-limited.
      */
     @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS)
-    public void setWidgetPreview(@NonNull ComponentName provider,
+    public boolean setWidgetPreview(@NonNull ComponentName provider,
             @AppWidgetProviderInfo.CategoryFlags int widgetCategories,
             @NonNull RemoteViews preview) {
         try {
-            mService.setWidgetPreview(provider, widgetCategories, preview);
+            return mService.setWidgetPreview(provider, widgetCategories, preview);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 70d2c7a..c7e5d88 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5081,8 +5081,6 @@
      * @see #getSystemService
      * @see android.hardware.face.FaceManager
      */
-    @FlaggedApi(android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION)
-    @SystemApi
     public static final String FACE_SERVICE = "face";
 
     /**
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6bb9c33..41c1f17 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -697,8 +697,9 @@
     public List<UserHandle> getProfiles() {
         if (mUserManager.isManagedProfile()
                 || (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()
-                        && android.os.Flags.allowPrivateProfile()
-                        && mUserManager.isPrivateProfile())) {
+                    && android.os.Flags.allowPrivateProfile()
+                    && android.multiuser.Flags.enablePrivateSpaceFeatures()
+                    && mUserManager.isPrivateProfile())) {
             // If it's a managed or private profile, only return the current profile.
             final List result = new ArrayList(1);
             result.add(android.os.Process.myUserHandle());
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index ac80561..48a7cc9 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -184,3 +184,11 @@
     description: "Enable Private Space telephony and SMS intent redirection to the main user"
     bug: "325576602"
 }
+
+flag {
+    name: "block_private_space_creation"
+    namespace: "profile_experiences"
+    description: "Allow blocking private space creation based on specific conditions"
+    bug: "290333800"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index 4dcc517..a908456 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -163,14 +163,31 @@
     }
 
     /**
-     * Update the URI relative filter groups for a package. All previously existing groups
-     * will be cleared before the new groups will be applied.
+     * Update the URI relative filter groups for a package. The groups set using this API acts
+     * as an additional filtering layer during intent resolution. It does not replace any
+     * existing groups that have been added to the package's intent filters either using the
+     * {@link android.content.IntentFilter#addUriRelativeFilterGroup(UriRelativeFilterGroup)}
+     * API or defined in the manifest.
+     * <p>
+     * Groups can be indexed to any domain or can be indexed for all subdomains by prefixing the
+     * hostname with a wildcard (i.e. "*.example.com"). Priority will be first given to groups
+     * that are indexed to the specific subdomain of the intent's data URI followed by any groups
+     * indexed to wildcard subdomains. If the subdomain consists of more than one label, priority
+     * will decrease corresponding to the decreasing number of subdomain labels after the wildcard.
+     * For example "a.b.c.d" will match "*.b.c.d" before "*.c.d".
+     * <p>
+     * All previously existing groups set for a domain index using this API will be cleared when
+     * new groups are set.
      *
      * @param packageName The name of the package.
      * @param domainToGroupsMap A map of domains to a list of {@link UriRelativeFilterGroup}s that
      *                         should apply to them. Groups for each domain will replace any groups
-     *                         provided for that domain in a prior call to this method. Groups will
+     *                         provided for that domain in a prior call to this method. To clear
+     *                         existing groups, set the list to null or a empty list. Groups will
      *                         be evaluated in the order they are provided.
+     *
+     * @see UriRelativeFilterGroup
+     * @see android.content.IntentFilter
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 08b9064..4cdaaddd 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -727,7 +727,7 @@
      * Returns if camera privacy is enabled for a specific package.
      *
      * @param packageName The package to check
-     * @return boolean sensor privacy state.
+     * @return boolean camera privacy state.
      *
      * @hide
      */
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 8165d44..3ba8be4 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -28,10 +28,3 @@
   bug: "302735104"
 }
 
-flag {
-  name: "face_background_authentication"
-  namespace: "biometrics_framework"
-  description: "Feature flag for allowing face background authentication with USE_BACKGROUND_FACE_AUTHENTICATION."
-  bug: "318584190"
-}
-
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 066c45f..210ce2b 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -18,23 +18,18 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_BIOMETRIC;
-import static android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
-import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION;
 
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricStateListener;
 import android.hardware.biometrics.CryptoObject;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
@@ -42,9 +37,9 @@
 import android.os.CancellationSignal;
 import android.os.CancellationSignal.OnCancelListener;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -54,21 +49,15 @@
 import android.view.Surface;
 
 import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 /**
  * A class that coordinates access to the face authentication hardware.
- *
- * <p>Please use {@link BiometricPrompt} for face authentication unless the experience must be
- * customized for unique system-level utilities, like the lock screen or ambient background usage.
- *
  * @hide
  */
-@FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
-@SystemApi
 @SystemService(Context.FACE_SERVICE)
 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
 
@@ -99,76 +88,81 @@
     @Nullable private GenerateChallengeCallback mGenerateChallengeCallback;
     private CryptoObject mCryptoObject;
     private Face mRemovalFace;
-    private Executor mExecutor;
+    private Handler mHandler;
     private List<FaceSensorPropertiesInternal> mProps = new ArrayList<>();
 
     private final IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
 
         @Override // binder call
         public void onEnrollResult(Face face, int remaining) {
-            mExecutor.execute(() -> sendEnrollResult(face, remaining));
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, face).sendToTarget();
         }
 
         @Override // binder call
         public void onAcquired(int acquireInfo, int vendorCode) {
-            mExecutor.execute(() -> sendAcquiredResult(acquireInfo, vendorCode));
+            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode).sendToTarget();
         }
 
         @Override // binder call
         public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
-            mExecutor.execute(() -> sendAuthenticatedSucceeded(face, userId, isStrongBiometric));
+            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId,
+                    isStrongBiometric ? 1 : 0, face).sendToTarget();
         }
 
         @Override // binder call
         public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
-            mExecutor.execute(() -> sendFaceDetected(sensorId, userId, isStrongBiometric));
+            mHandler.obtainMessage(MSG_FACE_DETECTED, sensorId, userId, isStrongBiometric)
+                    .sendToTarget();
         }
 
         @Override // binder call
         public void onAuthenticationFailed() {
-            mExecutor.execute(() -> sendAuthenticatedFailed());
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
         }
 
         @Override // binder call
         public void onError(int error, int vendorCode) {
-            mExecutor.execute(() -> sendErrorResult(error, vendorCode));
+            mHandler.obtainMessage(MSG_ERROR, error, vendorCode).sendToTarget();
         }
 
         @Override // binder call
         public void onRemoved(Face face, int remaining) {
-            mExecutor.execute(() -> {
-                sendRemovedResult(face, remaining);
-                if (remaining == 0) {
-                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                            Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0,
-                            UserHandle.USER_CURRENT);
-                }
-            });
+            mHandler.obtainMessage(MSG_REMOVED, remaining, 0, face).sendToTarget();
+            if (remaining == 0) {
+                Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0,
+                        UserHandle.USER_CURRENT);
+            }
         }
 
         @Override
         public void onFeatureSet(boolean success, int feature) {
-            mExecutor.execute(() -> sendSetFeatureCompleted(success, feature));
+            mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
         }
 
         @Override
         public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
-            mExecutor.execute(() -> sendGetFeatureCompleted(success, features, featureState));
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = success;
+            args.arg2 = features;
+            args.arg3 = featureState;
+            mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
         }
 
         @Override
         public void onChallengeGenerated(int sensorId, int userId, long challenge) {
-            mExecutor.execute(() -> sendChallengeGenerated(sensorId, userId, challenge));
+            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
+                    .sendToTarget();
         }
 
         @Override
         public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
-            mExecutor.execute(() -> sendAuthenticationFrame(frame));
+            mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
         }
 
         @Override
         public void onEnrollmentFrame(FaceEnrollFrame frame) {
-            mExecutor.execute(() -> sendEnrollmentFrame(frame));
+            mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget();
         }
     };
 
@@ -181,7 +175,7 @@
         if (mService == null) {
             Slog.v(TAG, "FaceAuthenticationManagerService was null");
         }
-        mExecutor = context.getMainExecutor();
+        mHandler = new MyHandler(context);
         if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
                 == PackageManager.PERMISSION_GRANTED) {
             addAuthenticatorsRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
@@ -195,16 +189,18 @@
     }
 
     /**
-     * Returns an {@link Executor} for the given {@link Handler} or the main {@link Executor} if
-     * {@code handler} is {@code null}.
+     * Use the provided handler thread for events.
      */
-    private @NonNull Executor createExecutorForHandlerIfNeeded(@Nullable Handler handler) {
-        return handler != null ? new HandlerExecutor(handler) : mContext.getMainExecutor();
+    private void useHandler(Handler handler) {
+        if (handler != null) {
+            mHandler = new MyHandler(handler.getLooper());
+        } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+            mHandler = new MyHandler(mContext.getMainLooper());
+        }
     }
 
     /**
      * @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FaceAuthenticateOptions)}.
-     * @hide
      */
     @Deprecated
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -216,22 +212,17 @@
     }
 
     /**
-     * Request authentication.
-     *
-     * <p>This call operates the face recognition hardware and starts capturing images.
+     * Request authentication. This call operates the face recognition hardware and starts capturing images.
      * It terminates when
      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
      * which point the object is no longer valid. The operation can be canceled by using the
-     * provided {@code cancel} object.
+     * provided cancel object.
      *
-     * @param crypto   the cryptographic operations to use for authentication or {@code null} if
-     *                 none required
-     * @param cancel   an object that can be used to cancel authentication or {@code null} if not
-     *                 needed
+     * @param crypto   object associated with the call or null if none required
+     * @param cancel   an object that can be used to cancel authentication
      * @param callback an object to receive authentication events
-     * @param handler  an optional handler to handle callback events or {@code null} to obtain main
-     *                 {@link Executor} from {@link Context}
+     * @param handler  an optional handler to handle callback events
      * @param options  additional options to customize this request
      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
      *                                  by
@@ -244,14 +235,6 @@
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             @NonNull AuthenticationCallback callback, @Nullable Handler handler,
             @NonNull FaceAuthenticateOptions options) {
-        authenticate(crypto, cancel, callback, createExecutorForHandlerIfNeeded(handler),
-                options, false /* allowBackgroundAuthentication */);
-    }
-
-    @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL, USE_BACKGROUND_FACE_AUTHENTICATION})
-    private void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, @NonNull Executor executor,
-            @NonNull FaceAuthenticateOptions options, boolean allowBackgroundAuthentication) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -266,15 +249,13 @@
 
         if (mService != null) {
             try {
-                mExecutor = executor;
+                useHandler(handler);
                 mAuthenticationCallback = callback;
                 mCryptoObject = crypto;
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
                 Trace.beginSection("FaceManager#authenticate");
-                final long authId = allowBackgroundAuthentication
-                        ? mService.authenticateInBackground(
-                                mToken, operationId, mServiceReceiver, options)
-                        : mService.authenticate(mToken, operationId, mServiceReceiver, options);
+                final long authId = mService.authenticate(
+                        mToken, operationId, mServiceReceiver, options);
                 if (cancel != null) {
                     cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
                 }
@@ -292,67 +273,6 @@
     }
 
     /**
-     * Request background face authentication.
-     *
-     * <p>This call operates the face recognition hardware and starts capturing images.
-     * It terminates when
-     * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
-     * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationSucceeded(
-     * BiometricPrompt.AuthenticationResult)} is called, at which point the object is no longer
-     * valid. The operation can be canceled by using the provided cancel object.
-     *
-     * <p>See {@link BiometricPrompt#authenticate} for more details. Please use
-     * {@link BiometricPrompt} for face authentication unless the experience must be customized for
-     * unique system-level utilities, like the lock screen or ambient background usage.
-     *
-     * @param executor the specified {@link Executor} to handle callback events; if {@code null},
-     *                 the callback will be executed on the main {@link Executor}.
-     * @param crypto   the cryptographic operations to use for authentication or {@code null} if
-     *                 none required.
-     * @param cancel   an object that can be used to cancel authentication or {@code null} if not
-     *                 needed.
-     * @param callback an object to receive authentication events.
-     * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
-     *                                  by
-     *                                  <a href="{@docRoot}training/articles/keystore.html">Android
-     *                                  Keystore facility</a>.
-     * @hide
-     */
-    @RequiresPermission(USE_BACKGROUND_FACE_AUTHENTICATION)
-    @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
-    @SystemApi
-    public void authenticateInBackground(@Nullable Executor executor,
-            @Nullable BiometricPrompt.CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull BiometricPrompt.AuthenticationCallback callback) {
-        authenticate(crypto, cancel, new AuthenticationCallback() {
-                    @Override
-                    public void onAuthenticationError(int errorCode, CharSequence errString) {
-                        callback.onAuthenticationError(errorCode, errString);
-                    }
-
-                    @Override
-                    public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
-                        callback.onAuthenticationHelp(helpCode, helpString);
-                    }
-
-                    @Override
-                    public void onAuthenticationSucceeded(AuthenticationResult result) {
-                        callback.onAuthenticationSucceeded(
-                                new BiometricPrompt.AuthenticationResult(
-                                        crypto,
-                                        BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC));
-                    }
-
-                    @Override
-                    public void onAuthenticationFailed() {
-                        callback.onAuthenticationFailed();
-                    }
-                }, executor == null ? mContext.getMainExecutor() : executor,
-                new FaceAuthenticateOptions.Builder().build(),
-                true /* allowBackgroundAuthentication */);
-    }
-
-    /**
      * Uses the face hardware to detect for the presence of a face, without giving details about
      * accept/reject/lockout.
      * @hide
@@ -710,14 +630,12 @@
     }
 
     /**
-     * Determine if there are enrolled {@link Face} templates.
+     * Determine if there is a face enrolled.
      *
-     * @return {@code true} if there are enrolled {@link Face} templates, {@code false} otherwise
+     * @return true if a face is enrolled, false otherwise
      * @hide
      */
-    @RequiresPermission(anyOf = {USE_BIOMETRIC_INTERNAL, USE_BACKGROUND_FACE_AUTHENTICATION})
-    @FlaggedApi(FLAG_FACE_BACKGROUND_AUTHENTICATION)
-    @SystemApi
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
     public boolean hasEnrolledTemplates() {
         return hasEnrolledTemplates(UserHandle.myUserId());
     }
@@ -882,7 +800,7 @@
                                             PowerManager.PARTIAL_WAKE_LOCK,
                                             "faceLockoutResetCallback");
                                     wakeLock.acquire();
-                                    mExecutor.execute(() -> {
+                                    mHandler.post(() -> {
                                         try {
                                             callback.onLockoutReset(sensorId);
                                         } finally {
@@ -1352,6 +1270,70 @@
         }
     }
 
+    private class MyHandler extends Handler {
+        private MyHandler(Context context) {
+            super(context.getMainLooper());
+        }
+
+        private MyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(android.os.Message msg) {
+            Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
+            switch (msg.what) {
+                case MSG_ENROLL_RESULT:
+                    sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
+                    break;
+                case MSG_ACQUIRED:
+                    sendAcquiredResult(msg.arg1 /* acquire info */, msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_AUTHENTICATION_SUCCEEDED:
+                    sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */,
+                            msg.arg2 == 1 /* isStrongBiometric */);
+                    break;
+                case MSG_AUTHENTICATION_FAILED:
+                    sendAuthenticatedFailed();
+                    break;
+                case MSG_ERROR:
+                    sendErrorResult(msg.arg1 /* errMsgId */, msg.arg2 /* vendorCode */);
+                    break;
+                case MSG_REMOVED:
+                    sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
+                    break;
+                case MSG_SET_FEATURE_COMPLETED:
+                    sendSetFeatureCompleted((boolean) msg.obj /* success */,
+                            msg.arg1 /* feature */);
+                    break;
+                case MSG_GET_FEATURE_COMPLETED:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    sendGetFeatureCompleted((boolean) args.arg1 /* success */,
+                            (int[]) args.arg2 /* features */,
+                            (boolean[]) args.arg3 /* featureState */);
+                    args.recycle();
+                    break;
+                case MSG_CHALLENGE_GENERATED:
+                    sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+                            (long) msg.obj /* challenge */);
+                    break;
+                case MSG_FACE_DETECTED:
+                    sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+                            (boolean) msg.obj /* isStrongBiometric */);
+                    break;
+                case MSG_AUTHENTICATION_FRAME:
+                    sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
+                    break;
+                case MSG_ENROLLMENT_FRAME:
+                    sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */);
+                    break;
+                default:
+                    Slog.w(TAG, "Unknown message: " + msg.what);
+            }
+            Trace.endSection();
+        }
+    }
+
     private void sendSetFeatureCompleted(boolean success, int feature) {
         if (mSetFeatureCallback == null) {
             return;
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index b98c0cb..553d9f7 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -47,7 +47,7 @@
     byte[] dumpSensorServiceStateProto(int sensorId, boolean clearSchedulerBuffer);
 
     // Retrieve static sensor properties for all face sensors
-    @EnforcePermission(anyOf = {"USE_BIOMETRIC_INTERNAL", "USE_BACKGROUND_FACE_AUTHENTICATION"})
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
 
     // Retrieve static sensor properties for the specified sensor
@@ -59,11 +59,6 @@
     long authenticate(IBinder token, long operationId, IFaceServiceReceiver receiver,
             in FaceAuthenticateOptions options);
 
-    // Authenticate with a face. A requestId is returned that can be used to cancel this operation.
-    @EnforcePermission("USE_BACKGROUND_FACE_AUTHENTICATION")
-    long authenticateInBackground(IBinder token, long operationId, IFaceServiceReceiver receiver,
-            in FaceAuthenticateOptions options);
-
     // Uses the face hardware to detect for the presence of a face, without giving details
     // about accept/reject/lockout. A requestId is returned that can be used to cancel this
     // operation.
@@ -138,7 +133,7 @@
     void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge);
 
     // Determine if a user has at least one enrolled face
-    @EnforcePermission(anyOf = {"USE_BIOMETRIC_INTERNAL", "USE_BACKGROUND_FACE_AUTHENTICATION"})
+    @EnforcePermission("USE_BIOMETRIC_INTERNAL")
     boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName);
 
     // Return the LockoutTracker status for the specified user
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 800ba6d..23d6007 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -85,6 +85,7 @@
     boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable, int mUserId);
     boolean isRestricted(int userId);
     boolean canHaveRestrictedProfile(int userId);
+    boolean canAddPrivateProfile(int userId);
     int getUserSerialNumber(int userId);
     int getUserHandle(int userSerialNumber);
     int getUserRestrictionSource(String restrictionKey, int userId);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9757a10..fdaa0b4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2353,6 +2353,17 @@
     public static final int USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS = 7;
 
     /**
+     * Indicates user operation failed because user is disabled on the device.
+     * @hide
+     */
+    public static final int USER_OPERATION_ERROR_DISABLED_USER = 8;
+    /**
+     * Indicates user operation failed because user is disabled on the device.
+     * @hide
+     */
+    public static final int USER_OPERATION_ERROR_PRIVATE_PROFILE = 9;
+
+    /**
      * Result returned from various user operations.
      *
      * @hide
@@ -2366,7 +2377,9 @@
             USER_OPERATION_ERROR_CURRENT_USER,
             USER_OPERATION_ERROR_LOW_STORAGE,
             USER_OPERATION_ERROR_MAX_USERS,
-            USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS
+            USER_OPERATION_ERROR_USER_ACCOUNT_ALREADY_EXISTS,
+            USER_OPERATION_ERROR_DISABLED_USER,
+            USER_OPERATION_ERROR_PRIVATE_PROFILE,
     })
     public @interface UserOperationResult {}
 
@@ -2563,6 +2576,17 @@
     }
 
     /**
+     * Returns whether the device supports Private Profile
+     * @hide
+     */
+    public static boolean isPrivateProfileEnabled() {
+        if (android.multiuser.Flags.blockPrivateSpaceCreation()) {
+            return !ActivityManager.isLowRamDeviceStatic();
+        }
+        return true;
+    }
+
+    /**
      * Returns whether multiple admins are enabled on the device
      * @hide
      */
@@ -3155,6 +3179,27 @@
     }
 
     /**
+     * Checks if it's possible to add a private profile to the context user
+     * @return whether the context user can add a private profile.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS},
+            conditional = true)
+    @UserHandleAware
+    public boolean canAddPrivateProfile() {
+        if (android.multiuser.Flags.blockPrivateSpaceCreation()) {
+            try {
+                return mService.canAddPrivateProfile(mUserId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns whether the context user has at least one restricted profile associated with it.
      * @return whether the user has a restricted profile associated with it
      * @hide
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index a14a2c7..555a120 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -70,6 +70,8 @@
 
     private final boolean mDefaultKeyboardVibrationEnabled;
 
+    private final boolean mHasFixedKeyboardAmplitude;
+
     /** @hide */
     public VibrationConfig(@Nullable Resources resources) {
         mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
@@ -87,6 +89,8 @@
                 com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
         mDefaultKeyboardVibrationEnabled = loadBoolean(resources,
                 com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true);
+        mHasFixedKeyboardAmplitude = loadFloat(resources,
+                com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude, -1) > 0;
 
         mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
                 com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -197,6 +201,14 @@
         return mDefaultKeyboardVibrationEnabled;
     }
 
+    /**
+     * Whether the device has a fixed amplitude for keyboard.
+     * @hide
+     */
+    public boolean hasFixedKeyboardAmplitude() {
+        return mHasFixedKeyboardAmplitude;
+    }
+
     /** Get the default vibration intensity for given usage. */
     @VibrationIntensity
     public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 410f510..3c7692d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1800,7 +1800,11 @@
      */
     @SystemApi
     @NonNull
-    @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+            android.Manifest.permission.GET_RUNTIME_PERMISSIONS
+    })
     @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
     public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
             @NonNull String persistentDeviceId) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cdb35ff..e26dc73 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3583,6 +3583,12 @@
                                                     + " and user:" + userHandle
                                                     + " with index:" + index);
                                         }
+                                        // Always make sure to close any pre-existing tracker before
+                                        // replacing it, to prevent memory leaks
+                                        var oldTracker = mGenerationTrackers.get(name);
+                                        if (oldTracker != null) {
+                                            oldTracker.destroy();
+                                        }
                                         mGenerationTrackers.put(name, new GenerationTracker(name,
                                                 array, index, generation,
                                                 mGenerationTrackerErrorHandler));
@@ -3805,6 +3811,12 @@
                                         + " in package:" + cr.getPackageName()
                                         + " with index:" + index);
                             }
+                            // Always make sure to close any pre-existing tracker before
+                            // replacing it, to prevent memory leaks
+                            var oldTracker = mGenerationTrackers.get(prefix);
+                            if (oldTracker != null) {
+                                oldTracker.destroy();
+                            }
                             mGenerationTrackers.put(prefix,
                                     new GenerationTracker(prefix, array, index, generation,
                                             mGenerationTrackerErrorHandler));
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 828004b..a6f380d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
@@ -5629,7 +5630,7 @@
     @Nullable
     private ViewTranslationCallback mViewTranslationCallback;
 
-    private float mFrameContentVelocity = 0;
+    private float mFrameContentVelocity = -1;
 
     @Nullable
 
@@ -5660,6 +5661,9 @@
     protected long mMinusTwoFrameIntervalMillis = 0;
     private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
 
+    private float mLastFrameX = Float.NaN;
+    private float mLastFrameY = Float.NaN;
+
     @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
     public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN;
     @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -24597,7 +24601,10 @@
     public void draw(@NonNull Canvas canvas) {
         final int privateFlags = mPrivateFlags;
         mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
-        mFrameContentVelocity = 0;
+
+        mFrameContentVelocity = -1;
+        mLastFrameX = mLeft + mRenderNode.getTranslationX();
+        mLastFrameY = mTop + mRenderNode.getTranslationY();
 
         /*
          * Draw traversal performs several drawing steps which must be executed
@@ -33673,6 +33680,17 @@
             if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                 viewRootImpl.recordViewPercentage(sizePercentage);
             }
+            if (viewVelocityApi()) {
+                float velocity = mFrameContentVelocity;
+                if (velocity < 0f) {
+                    velocity = calculateVelocity();
+                }
+                if (velocity > 0f) {
+                    float frameRate = convertVelocityToFrameRate(velocity);
+                    viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE);
+                    return;
+                }
+            }
             if (!Float.isNaN(mPreferredFrameRate)) {
                 if (mPreferredFrameRate < 0) {
                     if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
@@ -33695,6 +33713,23 @@
         }
     }
 
+    private float convertVelocityToFrameRate(float velocityPps) {
+        float density = getResources().getDisplayMetrics().density;
+        float velocityDps = velocityPps / density;
+        // Choose a frame rate in increments of 10fps
+        return Math.min(140f, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
+    }
+
+    private float calculateVelocity() {
+        // This current calculation is very simple. If something on the screen moved, then
+        // it votes for the highest velocity. If it doesn't move, then return 0.
+        float x = mLeft + mRenderNode.getTranslationX();
+        float y = mTop + mRenderNode.getTranslationY();
+
+        return (!Float.isNaN(mLastFrameX) && (x != mLastFrameX || y != mLastFrameY))
+                ? 100_000f : 0f;
+    }
+
     /**
      * Set the current velocity of the View, we only track positive value.
      * We will use the velocity information to adjust the frame rate when applicable.
@@ -33725,7 +33760,7 @@
     @FlaggedApi(FLAG_VIEW_VELOCITY_API)
     public float getFrameContentVelocity() {
         if (viewVelocityApi()) {
-            return mFrameContentVelocity;
+            return (mFrameContentVelocity < 0f) ? 0f : mFrameContentVelocity;
         }
         return 0;
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 02f8e6e..42f6405 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -31,6 +31,7 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -7571,7 +7572,8 @@
             }
 
             // For the variable refresh rate project
-            if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
+            if (handled && shouldTouchBoost(action & MotionEvent.ACTION_MASK,
+                    mWindowAttributes.type)) {
                 // set the frame rate to the maximum value.
                 mIsTouchBoosting = true;
                 setPreferredFrameRateCategory(mPreferredFrameRateCategory);
@@ -12398,6 +12400,17 @@
                     mFrameRateCompatibility).applyAsyncUnsafe();
                 mLastPreferredFrameRate = preferredFrameRate;
             }
+            if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && mIsTouchBoosting) {
+                // We've received a velocity, so we'll let the velocity control the
+                // frame rate unless we receive additional motion events.
+                mIsTouchBoosting = false;
+                if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                    Trace.instant(
+                            Trace.TRACE_TAG_VIEW,
+                            "ViewRootImpl#setFrameRate velocity used, no touch boost on next frame"
+                    );
+                }
+            }
         } catch (Exception e) {
             Log.e(mTag, "Unable to set frame rate", e);
         } finally {
@@ -12423,9 +12436,8 @@
     }
 
     private boolean shouldTouchBoost(int motionEventAction, int windowType) {
-        boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
-                || motionEventAction == MotionEvent.ACTION_MOVE
-                || motionEventAction == MotionEvent.ACTION_UP;
+        // boost for almost all input
+        boolean desiredAction = motionEventAction != MotionEvent.ACTION_OUTSIDE;
         boolean undesiredType = windowType == TYPE_INPUT_METHOD
                 && sToolkitFrameRateTypingReadOnlyFlagValue;
         // use toolkitSetFrameRate flag to gate the change
@@ -12531,6 +12543,14 @@
     }
 
     /**
+     * Returns whether touch boost is currently enabled.
+     */
+    @VisibleForTesting
+    public boolean getIsTouchBoosting() {
+        return mIsTouchBoosting;
+    }
+
+    /**
      * Get the value of mFrameRateCompatibility
      */
     @VisibleForTesting
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 131fca7..1efd375 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -315,7 +315,8 @@
     /**
      * Add to this view's child count.  This increases the current child count by
      * <var>num</var> children beyond what was last set by {@link #setChildCount}
-     * or {@link #addChildCount}.  The index at which the new child starts in the child
+     * or {@link #addChildCount}.  The index at which the new
+     * child starts in the child
      * array is returned.
      *
      * @param num The number of new children to add.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7dcbbea..78f06b6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -2655,7 +2655,8 @@
 
     private boolean privateSpaceEnabled() {
         return mIsIntentPicker && android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.allowResolverSheetForPrivateSpace();
+                && android.multiuser.Flags.allowResolverSheetForPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
     /**
diff --git a/core/java/com/android/internal/app/SetScreenLockDialogActivity.java b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
index 93fe37c..360fcaf 100644
--- a/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
+++ b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
@@ -75,7 +75,8 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         if (!(android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.showSetScreenLockDialog())) {
+                && android.multiuser.Flags.showSetScreenLockDialog()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures())) {
             finish();
             return;
         }
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 4ef0a1b..97f8084 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -77,6 +77,7 @@
         }
 
         if (android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()
                 && !userManager.isManagedProfile(mUserId)) {
             Log.e(TAG, "Unlaunchable activity for target package " + targetPackageName
                     + " called for a non-managed-profile " + mUserId);
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 85bdbb9..e55cdef 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -80,11 +80,10 @@
             in Bundle extras, in IntentSender resultIntent);
     boolean isRequestPinAppWidgetSupported();
     oneway void noteAppWidgetTapped(in String callingPackage, in int appWidgetId);
-    void setWidgetPreview(in ComponentName providerComponent, in int widgetCategories,
+    boolean setWidgetPreview(in ComponentName providerComponent, in int widgetCategories,
             in RemoteViews preview);
     @nullable RemoteViews getWidgetPreview(in String callingPackage,
             in ComponentName providerComponent, in int profileId, in int widgetCategory);
     void removeWidgetPreview(in ComponentName providerComponent, in int widgetCategories);
-
 }
 
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index bd806bf..91678c7 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -560,6 +560,19 @@
      */
     public static final String CURSOR_HOVER_STATES_ENABLED = "cursor_hover_states_enabled";
 
+
+    /*
+     * (long) The reset interval for generated preview API calls.
+     */
+    public static final String GENERATED_PREVIEW_API_RESET_INTERVAL_MS =
+            "generated_preview_api_reset_interval_ms";
+
+    /*
+     * (int) The max number of generated preview API calls per reset interval.
+     */
+    public static final String GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL =
+            "generated_preview_api_max_calls_per_interval";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index bdd9a91..4ead82f 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -70,6 +70,8 @@
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData;
@@ -90,6 +92,8 @@
     private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
     private final TreeMap<String, IProtoLogGroup> mLogGroups;
 
+    private final ExecutorService mBackgroundLoggingService = Executors.newCachedThreadPool();
+
     public PerfettoProtoLogImpl(String viewerConfigFilePath,
             TreeMap<String, IProtoLogGroup> logGroups) {
         this(() -> {
@@ -134,7 +138,8 @@
 
         long tsNanos = SystemClock.elapsedRealtimeNanos();
         try {
-            logToProto(level, group.name(), messageHash, paramsMask, args, tsNanos);
+            mBackgroundLoggingService.submit(() ->
+                    logToProto(level, group.name(), messageHash, paramsMask, args, tsNanos));
             if (group.isLogToLogcat()) {
                 logToLogcat(group.getTag(), level, messageHash, messageString, args);
             }
@@ -462,40 +467,6 @@
     }
 
     /**
-     * Responds to a shell command.
-     */
-    public int onShellCommand(ShellCommand shell) {
-        PrintWriter pw = shell.getOutPrintWriter();
-        String cmd = shell.getNextArg();
-        if (cmd == null) {
-            return unknownCommand(pw);
-        }
-        ArrayList<String> args = new ArrayList<>();
-        String arg;
-        while ((arg = shell.getNextArg()) != null) {
-            args.add(arg);
-        }
-        final ILogger logger = (msg) -> logAndPrintln(pw, msg);
-        String[] groups = args.toArray(new String[args.size()]);
-        switch (cmd) {
-            case "enable-text":
-                return this.startLoggingToLogcat(groups, logger);
-            case "disable-text":
-                return this.stopLoggingToLogcat(groups, logger);
-            default:
-                return unknownCommand(pw);
-        }
-    }
-
-    private int unknownCommand(PrintWriter pw) {
-        pw.println("Unknown command");
-        pw.println("Window manager logging options:");
-        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
-        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
-        return -1;
-    }
-
-    /**
      * Returns {@code true} iff logging to proto is enabled.
      */
     public boolean isProtoEnabled() {
@@ -554,6 +525,49 @@
         return 0;
     }
 
+    /**
+     * Responds to a shell command.
+     */
+    public int onShellCommand(ShellCommand shell) {
+        PrintWriter pw = shell.getOutPrintWriter();
+        String cmd = shell.getNextArg();
+        if (cmd == null) {
+            return unknownCommand(pw);
+        }
+        ArrayList<String> args = new ArrayList<>();
+        String arg;
+        while ((arg = shell.getNextArg()) != null) {
+            args.add(arg);
+        }
+        final ILogger logger = (msg) -> logAndPrintln(pw, msg);
+        String[] groups = args.toArray(new String[0]);
+        switch (cmd) {
+            case "start", "stop" -> {
+                pw.println("Command not supported. "
+                        + "Please start and stop ProtoLog tracing with Perfetto.");
+                return -1;
+            }
+            case "enable-text" -> {
+                mViewerConfigReader.loadViewerConfig(logger);
+                return setTextLogging(true, logger, groups);
+            }
+            case "disable-text" -> {
+                return setTextLogging(false, logger, groups);
+            }
+            default -> {
+                return unknownCommand(pw);
+            }
+        }
+    }
+
+    private int unknownCommand(PrintWriter pw) {
+        pw.println("Unknown command");
+        pw.println("Window manager logging options:");
+        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
+        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
+        return -1;
+    }
+
     static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
         Slog.i(LOG_TAG, msg);
         if (pw != null) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9d80d153..1acdc75 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6772,13 +6772,6 @@
     <permission android:name="android.permission.USE_BIOMETRIC_INTERNAL"
         android:protectionLevel="signature" />
 
-    <!-- Allows privileged apps to access the background face authentication.
-        @SystemApi
-        @FlaggedApi("android.hardware.biometrics.face_background_authentication")
-        @hide -->
-    <permission android:name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION"
-        android:protectionLevel="signature|privileged" />
-
     <!-- Allows the system to control the BiometricDialog (SystemUI). Reserved for the system. @hide -->
     <permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
                 android:protectionLevel="signature" />
diff --git a/core/tests/coretests/res/layout/view_velocity_test.xml b/core/tests/coretests/res/layout/view_velocity_test.xml
new file mode 100644
index 0000000..98154a4
--- /dev/null
+++ b/core/tests/coretests/res/layout/view_velocity_test.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/frameLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <View
+        android:id="@+id/moving_view"
+        android:layout_width="50dp"
+        android:layout_height="50dp" />
+</FrameLayout>
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 34f5841..3a872b5 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -18,7 +18,6 @@
 
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
-import static android.hardware.biometrics.Flags.FLAG_FACE_BACKGROUND_AUTHENTICATION;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -36,15 +35,12 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
-import android.hardware.biometrics.BiometricPrompt;
 import android.os.CancellationSignal;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 
 import com.android.internal.R;
 
@@ -62,7 +58,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 @Presubmit
 @RunWith(MockitoJUnitRunner.class)
@@ -83,8 +78,6 @@
     @Mock
     private FaceManager.AuthenticationCallback mAuthCallback;
     @Mock
-    private BiometricPrompt.AuthenticationCallback mBioAuthCallback;
-    @Mock
     private FaceManager.EnrollmentCallback mEnrollmentCallback;
     @Mock
     private FaceManager.FaceDetectionCallback mFaceDetectionCallback;
@@ -98,16 +91,13 @@
     private TestLooper mLooper;
     private Handler mHandler;
     private FaceManager mFaceManager;
-    private Executor mExecutor;
 
     @Before
     public void setUp() throws Exception {
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
-        mExecutor = new HandlerExecutor(mHandler);
 
         when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
-        when(mContext.getMainExecutor()).thenReturn(mExecutor);
         when(mContext.getOpPackageName()).thenReturn(PACKAGE_NAME);
         when(mContext.getAttributionTag()).thenReturn(ATTRIBUTION_TAG);
         when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
@@ -169,19 +159,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(FLAG_FACE_BACKGROUND_AUTHENTICATION)
-    public void authenticateInBackground_errorWhenUnavailable() throws Exception {
-        when(mService.authenticateInBackground(any(), anyLong(), any(), any()))
-                .thenThrow(new RemoteException());
-
-        mFaceManager.authenticateInBackground(mExecutor, null, new CancellationSignal(),
-                mBioAuthCallback);
-        mLooper.dispatchAll();
-
-        verify(mBioAuthCallback).onAuthenticationError(eq(FACE_ERROR_HW_UNAVAILABLE), any());
-    }
-
-    @Test
     public void enrollment_errorWhenFaceEnrollmentExists() throws RemoteException {
         when(mResources.getInteger(R.integer.config_faceMaxTemplatesPerUser)).thenReturn(1);
         when(mService.getEnrolledFaces(anyInt(), anyInt(), anyString()))
diff --git a/core/tests/coretests/src/android/view/ViewVelocityTest.java b/core/tests/coretests/src/android/view/ViewVelocityTest.java
new file mode 100644
index 0000000..d437f7b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewVelocityTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewVelocityTest {
+
+    @Rule
+    public ActivityTestRule<ViewCaptureTestActivity> mActivityRule = new ActivityTestRule<>(
+            ViewCaptureTestActivity.class);
+
+    private Activity mActivity;
+    private View mMovingView;
+    private ViewRootImpl mViewRoot;
+
+    @Before
+    public void setUp() throws Throwable {
+        mActivity = mActivityRule.getActivity();
+        mActivityRule.runOnUiThread(() -> {
+            mActivity.setContentView(R.layout.view_velocity_test);
+            mMovingView = mActivity.findViewById(R.id.moving_view);
+        });
+        ViewParent parent = mActivity.getWindow().getDecorView().getParent();
+        while (parent instanceof View) {
+            parent = parent.getParent();
+        }
+        mViewRoot = (ViewRootImpl) parent;
+    }
+
+    @UiThreadTest
+    @Test
+    @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+    public void frameRateChangesWhenContentMoves() {
+        mMovingView.offsetLeftAndRight(100);
+        float frameRate = mViewRoot.getPreferredFrameRate();
+        assertTrue(frameRate > 0);
+    }
+
+    @UiThreadTest
+    @Test
+    @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+    public void firstFrameNoMovement() {
+        assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+    public void touchBoostDisable() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            long now = SystemClock.uptimeMillis();
+            MotionEvent down = MotionEvent.obtain(
+                    /* downTime */ now,
+                    /* eventTime */ now,
+                    /* action */ MotionEvent.ACTION_DOWN,
+                    /* x */ 0f,
+                    /* y */ 0f,
+                    /* metaState */ 0
+            );
+            mActivity.dispatchTouchEvent(down);
+            mMovingView.offsetLeftAndRight(10);
+        });
+        mActivityRule.runOnUiThread(() -> {
+            mMovingView.invalidate();
+        });
+
+        mActivityRule.runOnUiThread(() -> {
+            assertFalse(mViewRoot.getIsTouchBoosting());
+        });
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index b209c7c..cb8754a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -1162,7 +1162,8 @@
     @Test
     public void testTriggerFromPrivateProfile_withoutWorkProfile() throws RemoteException {
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         markPrivateProfileUserAvailable();
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> privateResolvedComponentInfos =
@@ -1183,7 +1184,8 @@
     @Test
     public void testTriggerFromPrivateProfile_withWorkProfilePresent(){
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         ResolverActivity.ENABLE_TABBED_VIEW = false;
         markPrivateProfileUserAvailable();
         markWorkProfileUserAvailable();
@@ -1205,7 +1207,8 @@
     @Test
     public void testPrivateProfile_triggerFromPrimaryUser_withWorkProfilePresent(){
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         markPrivateProfileUserAvailable();
         markWorkProfileUserAvailable();
         Intent sendIntent = createSendImageIntent();
@@ -1228,7 +1231,8 @@
     @Test
     public void testPrivateProfile_triggerFromWorkProfile(){
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ALLOW_RESOLVER_SHEET_FOR_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         markPrivateProfileUserAvailable();
         markWorkProfileUserAvailable();
         Intent sendIntent = createSendImageIntent();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 051e73f..0f12438 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -455,7 +455,7 @@
         <permission name="android.permission.USE_BIOMETRIC" />
         <permission name="android.permission.TEST_BIOMETRIC" />
         <permission name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
-        <permission name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION" />
+        <permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
         <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
         <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
         <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases -->
diff --git a/libs/WindowManager/Shell/res/drawable/circular_progress.xml b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
index 9482645..294b1f0 100644
--- a/libs/WindowManager/Shell/res/drawable/circular_progress.xml
+++ b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
@@ -25,7 +25,7 @@
             <shape
                 android:shape="ring"
                 android:thickness="3dp"
-                android:innerRadius="17dp"
+                android:innerRadius="14dp"
                 android:useLevel="true">
             </shape>
         </rotate>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
index 02b7075..e5fe1b54 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_desktop_mode_maximize_button_dark.xml
@@ -15,12 +15,12 @@
   ~ limitations under the License.
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="48dp"
-    android:height="48dp"
+    android:width="24dp"
+    android:height="24dp"
     android:tint="?attr/colorControlNormal"
     android:viewportHeight="960"
     android:viewportWidth="960">
     <path
-        android:fillColor="@android:color/white"
-        android:pathData="M180,840Q156,840 138,822Q120,804 120,780L120,180Q120,156 138,138Q156,120 180,120L780,120Q804,120 822,138Q840,156 840,180L840,780Q840,804 822,822Q804,840 780,840L180,840ZM180,780L780,780Q780,780 780,780Q780,780 780,780L780,277L180,277L180,780Q180,780 180,780Q180,780 180,780Z" />
+        android:fillColor="@android:color/black"
+        android:pathData="M160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,320L160,320L160,720Q160,720 160,720Q160,720 160,720Z"/>
 </vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml
deleted file mode 100644
index 7c4f499..0000000
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_select.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="20dp"
-    android:height="20dp"
-    android:viewportWidth="20"
-    android:viewportHeight="20">
-  <path
-      android:pathData="M15.701,14.583L18.567,17.5L17.425,18.733L14.525,15.833L12.442,17.917V12.5H17.917L15.701,14.583ZM15.833,5.833H17.5V7.5H15.833V5.833ZM17.5,4.167H15.833V2.567C16.75,2.567 17.5,3.333 17.5,4.167ZM12.5,2.5H14.167V4.167H12.5V2.5ZM15.833,9.167H17.5V10.833H15.833V9.167ZM7.5,17.5H5.833V15.833H7.5V17.5ZM4.167,7.5H2.5V5.833H4.167V7.5ZM4.167,2.567V4.167H2.5C2.5,3.333 3.333,2.567 4.167,2.567ZM4.167,14.167H2.5V12.5H4.167V14.167ZM7.5,4.167H5.833V2.5H7.5V4.167ZM10.833,4.167H9.167V2.5H10.833V4.167ZM10.833,17.5H9.167V15.833H10.833V17.5ZM4.167,10.833H2.5V9.167H4.167V10.833ZM4.167,17.567C3.25,17.567 2.5,16.667 2.5,15.833H4.167V17.567Z"
-      android:fillColor="#1C1C14"/>
-</vector>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index a5605a7..fa18e2b 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -80,11 +80,14 @@
 
     <com.android.wm.shell.windowdecor.MaximizeButtonView
         android:id="@+id/maximize_button_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="44dp"
+        android:layout_height="40dp"
         android:layout_gravity="end"
+        android:layout_marginHorizontal="8dp"
+        android:paddingHorizontal="5dp"
+        android:paddingVertical="3dp"
         android:clickable="true"
-        android:focusable="true" />
+        android:focusable="true"/>
 
     <ImageButton
         android:id="@+id/close_window"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index c6f85a0..fca2fe4 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -134,15 +134,6 @@
             android:drawableStart="@drawable/desktop_mode_ic_handle_menu_screenshot"
             android:drawableTint="?androidprv:attr/materialColorOnSurface"
             style="@style/DesktopModeHandleMenuActionButton"/>
-
-        <Button
-            android:id="@+id/select_button"
-            android:contentDescription="@string/select_text"
-            android:text="@string/select_text"
-            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_select"
-            android:drawableTint="?androidprv:attr/materialColorOnSurface"
-            style="@style/DesktopModeHandleMenuActionButton"/>
-
     </LinearLayout>
 </LinearLayout>
 
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index e0057fe..296c8956 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -20,16 +20,16 @@
         android:id="@+id/progress_bar"
         style="?android:attr/progressBarStyleHorizontal"
         android:progressDrawable="@drawable/circular_progress"
-        android:layout_width="40dp"
-        android:layout_height="40dp"
+        android:layout_width="34dp"
+        android:layout_height="34dp"
         android:indeterminate="false"
         android:visibility="invisible"/>
 
     <ImageButton
         android:id="@+id/maximize_window"
-        android:layout_width="40dp"
-        android:layout_height="40dp"
-        android:padding="9dp"
+        android:layout_width="34dp"
+        android:layout_height="34dp"
+        android:padding="5dp"
         android:contentDescription="@string/maximize_button_text"
         android:tint="?androidprv:attr/materialColorOnSurface"
         android:background="?android:selectableItemBackgroundBorderless"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 22ba708..7b84868 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -16,6 +16,10 @@
 
 package com.android.wm.shell.desktopmode;
 
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.os.SystemProperties;
 
 import com.android.window.flags.Flags;
@@ -66,6 +70,9 @@
     private static final boolean USE_ROUNDED_CORNERS = SystemProperties.getBoolean(
             "persist.wm.debug.desktop_use_rounded_corners", true);
 
+    private static final boolean ENFORCE_DISPLAY_RESTRICTIONS = SystemProperties.getBoolean(
+            "persist.wm.debug.desktop_mode_enforce_display_restrictions", true);
+
     /**
      * Return {@code true} if desktop windowing is enabled
      */
@@ -104,4 +111,21 @@
     public static boolean useRoundedCorners() {
         return USE_ROUNDED_CORNERS;
     }
+
+    /**
+     * Return whether the display size restrictions should be enforced.
+     */
+    public static boolean enforceDisplayRestrictions() {
+        return ENFORCE_DISPLAY_RESTRICTIONS;
+    }
+
+    /**
+     * Return {@code true} if the display associated with the task is at least of size
+     * {@link android.content.res.Configuration#SCREENLAYOUT_SIZE_XLARGE} or has been overridden to
+     * ignore the size constraint.
+     */
+    public static boolean meetsMinimumDisplayRequirements(@NonNull RunningTaskInfo taskInfo) {
+        return !enforceDisplayRestrictions()
+                || taskInfo.configuration.isLayoutSizeAtLeast(SCREENLAYOUT_SIZE_XLARGE);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 654409f..2c66fd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -305,6 +305,12 @@
             task: RunningTaskInfo,
             wct: WindowContainerTransaction = WindowContainerTransaction()
     ) {
+        if (!DesktopModeStatus.meetsMinimumDisplayRequirements(task)) {
+            KtProtoLog.w(
+                WM_SHELL_DESKTOP_MODE, "DesktopTasksController: Cannot enter desktop, " +
+                        "display does not meet minimum size requirements")
+            return
+        }
         KtProtoLog.v(
             WM_SHELL_DESKTOP_MODE,
             "DesktopTasksController: moveToDesktop taskId=%d",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index f4ccd68..bf22193 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -442,12 +442,6 @@
                 mDesktopTasksController.requestSplit(decoration.mTaskInfo);
             } else if (id == R.id.collapse_menu_button) {
                 decoration.closeHandleMenu();
-            } else if (id == R.id.select_button) {
-                if (DesktopModeStatus.IS_DISPLAY_CHANGE_ENABLED) {
-                    // TODO(b/278084491): dev option to enable display switching
-                    //  remove when select is implemented
-                    mDesktopTasksController.moveToNextDisplay(mTaskId);
-                }
             } else if (id == R.id.maximize_window) {
                 final RunningTaskInfo taskInfo = decoration.mTaskInfo;
                 decoration.closeHandleMenu();
@@ -1058,8 +1052,7 @@
                 && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
                 && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                 && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
-                && mDisplayController.getDisplayContext(taskInfo.displayId)
-                .getResources().getConfiguration().smallestScreenWidthDp >= 600;
+                && DesktopModeStatus.meetsMinimumDisplayRequirements(taskInfo);
     }
 
     private void createWindowDecoration(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index b37dd0d..3d0dd31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -35,7 +35,6 @@
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
-import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -53,6 +52,7 @@
  */
 class HandleMenu {
     private static final String TAG = "HandleMenu";
+    private static final boolean SHOULD_SHOW_MORE_ACTIONS_PILL = false;
     private final Context mContext;
     private final WindowDecoration mParentDecor;
     private WindowDecoration.AdditionalWindow mHandleMenuWindow;
@@ -185,11 +185,9 @@
      * Set up interactive elements & height of handle menu's more actions pill
      */
     private void setupMoreActionsPill(View handleMenu) {
-        final Button selectBtn = handleMenu.findViewById(R.id.select_button);
-        selectBtn.setOnClickListener(mOnClickListener);
-        final Button screenshotBtn = handleMenu.findViewById(R.id.screenshot_button);
-        // TODO: Remove once implemented.
-        screenshotBtn.setVisibility(View.GONE);
+        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+            handleMenu.findViewById(R.id.more_actions_pill).setVisibility(View.GONE);
+        }
     }
 
     /**
@@ -305,12 +303,15 @@
      * Determines handle menu height based on if windowing pill should be shown.
      */
     private int getHandleMenuHeight(Resources resources) {
-        int menuHeight = loadDimensionPixelSize(resources,
-                R.dimen.desktop_mode_handle_menu_height);
+        int menuHeight = loadDimensionPixelSize(resources, R.dimen.desktop_mode_handle_menu_height);
         if (!mShouldShowWindowingPill) {
             menuHeight -= loadDimensionPixelSize(resources,
                     R.dimen.desktop_mode_handle_menu_windowing_pill_height);
         }
+        if (!SHOULD_SHOW_MORE_ACTIONS_PILL) {
+            menuHeight -= loadDimensionPixelSize(resources,
+                    R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
+        }
         return menuHeight;
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 35c803b..4c8a308 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -23,6 +23,8 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE
 import android.os.Binder
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
@@ -123,7 +125,7 @@
 
     @Before
     fun setUp() {
-        mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
+        mockitoSession = mockitoSession().spyStatic(DesktopModeStatus::class.java).startMocking()
         whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
 
         shellInit = Mockito.spy(ShellInit(testExecutor))
@@ -332,6 +334,45 @@
     }
 
     @Test
+    fun moveToDesktop_screenSizeBelowXLarge_doesNothing() {
+        val task = setUpFullscreenTask()
+
+        // Update screen layout to be below minimum size
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+        controller.moveToDesktop(task)
+        verifyWCTNotExecuted()
+    }
+
+    @Test
+    fun moveToDesktop_screenSizeBelowXLarge_displayRestrictionsOverridden_taskIsMovedToDesktop() {
+        val task = setUpFullscreenTask()
+
+        // Update screen layout to be below minimum size
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+        // Simulate enforce display restrictions system property overridden to false
+        whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)
+
+        controller.moveToDesktop(task)
+
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    fun moveToDesktop_screenSizeXLarge_taskIsMovedToDesktop() {
+        val task = setUpFullscreenTask()
+
+        controller.moveToDesktop(task)
+
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
     fun moveToDesktop_otherFreeformTasksBroughtToFront() {
         val homeTask = setUpHomeTask()
         val freeformTask = setUpFreeformTask()
@@ -816,6 +857,7 @@
 
     private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createFullscreenTask(displayId)
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         runningTasks.add(task)
         return task
@@ -823,6 +865,7 @@
 
     private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createSplitScreenTask(displayId)
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
         whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         runningTasks.add(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 9bb5482..83519bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -23,10 +23,14 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.content.Context
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL
+import android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE
 import android.graphics.Rect
 import android.hardware.display.DisplayManager
 import android.hardware.display.VirtualDisplay
 import android.os.Handler
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.util.SparseArray
@@ -42,6 +46,9 @@
 import android.view.WindowInsets.Type.navigationBars
 import android.view.WindowInsets.Type.statusBars
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
@@ -51,6 +58,7 @@
 import com.android.wm.shell.common.DisplayLayout
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeStatus
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.sysui.KeyguardChangeListener
 import com.android.wm.shell.sysui.ShellCommandHandler
@@ -59,6 +67,7 @@
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
@@ -71,6 +80,7 @@
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 import java.util.Optional
 import java.util.function.Supplier
 import org.mockito.Mockito
@@ -82,6 +92,10 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
 class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
+    @JvmField
+    @Rule
+    val setFlagsRule = SetFlagsRule()
+
     @Mock private lateinit var mockDesktopModeWindowDecorFactory:
             DesktopModeWindowDecoration.Factory
     @Mock private lateinit var mockMainHandler: Handler
@@ -351,6 +365,54 @@
         inOrder.verify(windowDecorByTaskIdSpy).remove(task.taskId)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    fun testWindowDecor_screenSizeBelowXLarge_decorNotCreated() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        // Update screen layout to be below minimum size
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+
+        onTaskOpening(task)
+        verify(mockDesktopModeWindowDecorFactory, never())
+            .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    fun testWindowDecor_screenSizeBelowXLarge_displayRestrictionsOverridden_decorCreated() {
+        val mockitoSession: StaticMockitoSession = mockitoSession()
+            .strictness(Strictness.LENIENT)
+            .spyStatic(DesktopModeStatus::class.java)
+            .startMocking()
+        try {
+            // Simulate enforce display restrictions system property overridden to false
+            whenever(DesktopModeStatus.enforceDisplayRestrictions()).thenReturn(false)
+
+            val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+            // Update screen layout to be below minimum size
+            task.configuration.screenLayout = SCREENLAYOUT_SIZE_NORMAL
+            setUpMockDecorationsForTasks(task)
+
+            onTaskOpening(task)
+            verify(mockDesktopModeWindowDecorFactory)
+                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+        } finally {
+            mockitoSession.finishMocking()
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    fun testWindowDecor_screenSizeXLarge_decorCreated() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+        task.configuration.screenLayout = SCREENLAYOUT_SIZE_XLARGE
+        setUpMockDecorationsForTasks(task)
+
+        onTaskOpening(task)
+        verify(mockDesktopModeWindowDecorFactory)
+            .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+    }
+
     private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
         desktopModeWindowDecorViewModel.onTaskOpening(
                 task,
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp b/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp
new file mode 100644
index 0000000..4b008a7
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp
@@ -0,0 +1,51 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
+cc_fuzz {
+    name: "resxmlparser_fuzzer",
+    srcs: [
+        "resxmlparser_fuzzer.cpp",
+    ],
+    host_supported: true,
+
+    static_libs: ["libgmock"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        darwin: {
+            // libbinder is not supported on mac
+            enabled: false,
+        },
+    },
+
+    include_dirs: [
+        "system/incremental_delivery/incfs/util/include/",
+    ],
+
+    corpus: ["testdata/*"],
+    dictionary: "xmlparser_fuzzer.dict",
+}
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
new file mode 100644
index 0000000..829a396
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <cstdint>
+#include <cstddef>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "androidfw/ResourceTypes.h"
+
+static void populateDynamicRefTableWithFuzzedData(
+    android::DynamicRefTable& table,
+    FuzzedDataProvider& fuzzedDataProvider) {
+
+    const size_t numMappings = fuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, 5);
+    for (size_t i = 0; i < numMappings; ++i) {
+        const uint8_t packageId = fuzzedDataProvider.ConsumeIntegralInRange<uint8_t>(0x02, 0x7F);
+
+        // Generate a package name
+        std::string packageName;
+        size_t packageNameLength = fuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, 128);
+        for (size_t j = 0; j < packageNameLength; ++j) {
+            // Consume characters only in the ASCII range (0x20 to 0x7E) to ensure valid UTF-8
+            char ch = fuzzedDataProvider.ConsumeIntegralInRange<char>(0x20, 0x7E);
+            packageName.push_back(ch);
+        }
+
+        // Convert std::string to String16 for compatibility
+        android::String16 androidPackageName(packageName.c_str(), packageName.length());
+
+        // Add the mapping to the table
+        table.addMapping(androidPackageName, packageId);
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fuzzedDataProvider(data, size);
+
+    auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
+
+    // Populate the DynamicRefTable with fuzzed data
+    populateDynamicRefTableWithFuzzedData(*dynamic_ref_table, fuzzedDataProvider);
+
+    auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
+
+    std::vector<uint8_t> xmlData = fuzzedDataProvider.ConsumeRemainingBytes<uint8_t>();
+    if (tree.setTo(xmlData.data(), xmlData.size()) != android::NO_ERROR) {
+        return 0; // Exit early if unable to parse XML data
+    }
+
+    tree.restart();
+
+    size_t len = 0;
+    auto code = tree.next();
+    if (code == android::ResXMLParser::START_TAG) {
+        // Access element name
+        auto name = tree.getElementName(&len);
+
+        // Access attributes of the current element
+        for (size_t i = 0; i < tree.getAttributeCount(); i++) {
+            // Access attribute name
+            auto attrName = tree.getAttributeName(i, &len);
+        }
+    } else if (code == android::ResXMLParser::TEXT) {
+        const auto text = tree.getText(&len);
+    }
+    return 0; // Non-zero return values are reserved for future use.
+}
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml
new file mode 100644
index 0000000..417fec7
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <child id="1">
+        <subchild type="A">Content A</subchild>
+        <subchild type="B">Content B</subchild>
+    </child>
+    <child id="2" extra="data">
+        <subchild type="C">Content C</subchild>
+    </child>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml
new file mode 100644
index 0000000..7e13db5
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <child1>Value 1</child1>
+    <child2>Value 2</child2>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml
new file mode 100644
index 0000000..90cdf35
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <!-- Example with special characters and CDATA -->
+    <data><![CDATA[Some <encoded> data & other "special" characters]]></data>
+    <message>Hello &amp; Welcome!</message>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict b/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict
new file mode 100644
index 0000000..745ded4
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict
@@ -0,0 +1,11 @@
+root_tag=<root>
+child_tag=<child>
+end_child_tag=</child>
+id_attr=id="
+type_attr=type="
+cdata_start=<![CDATA[
+cdata_end=]]>
+ampersand_entity=&amp;
+xml_header=<?xml version="1.0" encoding="UTF-8"?>
+comment_start=<!--
+comment_end= -->
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 6ced91f..b5f7caa 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -25,7 +25,6 @@
 #include <android/sync.h>
 #include <gui/TraceUtils.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
 #include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
 #include <include/gpu/ganesh/vk/GrVkDirectContext.h>
 #include <ui/FatVector.h>
@@ -598,14 +597,15 @@
                         close(fence_clone);
                         sync_wait(bufferInfo->dequeue_fence, -1 /* forever */);
                     } else {
-                        GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(semaphore);
+                        GrBackendSemaphore backendSemaphore;
+                        backendSemaphore.initVulkan(semaphore);
                         // Skia will take ownership of the VkSemaphore and delete it once the wait
                         // has finished. The VkSemaphore also owns the imported fd, so it will
                         // close the fd when it is deleted.
-                        bufferInfo->skSurface->wait(1, &beSemaphore);
+                        bufferInfo->skSurface->wait(1, &backendSemaphore);
                         // The following flush blocks the GPU immediately instead of waiting for
                         // other drawing ops. It seems dequeue_fence is not respected otherwise.
-                        // TODO: remove the flush after finding why beSemaphore is not working.
+                        // TODO: remove the flush after finding why backendSemaphore is not working.
                         skgpu::ganesh::FlushAndSubmit(bufferInfo->skSurface.get());
                     }
                 }
@@ -625,8 +625,8 @@
 
     SharedSemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
                         VkSemaphore semaphore)
-            : mDestroyFunction(destroyFunction), mDevice(device),
-              mGrBackendSemaphore(GrBackendSemaphores::MakeVk(semaphore)) {
+            : mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {
+        mGrBackendSemaphore.initVulkan(semaphore);
     }
 
     ~SharedSemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
@@ -798,7 +798,8 @@
         return UNKNOWN_ERROR;
     }
 
-    GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(semaphore);
+    GrBackendSemaphore beSemaphore;
+    beSemaphore.initVulkan(semaphore);
 
     // Skia will take ownership of the VkSemaphore and delete it once the wait has finished. The
     // VkSemaphore also owns the imported fd, so it will close the fd when it is deleted.
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 3363ac0..d7a2e36 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -21,8 +21,6 @@
 import android.content.Context
 import android.credentials.CredentialManager
 import android.credentials.GetCredentialRequest
-import android.credentials.GetCredentialResponse
-import android.credentials.GetCredentialException
 import android.credentials.GetCandidateCredentialsResponse
 import android.credentials.GetCandidateCredentialsException
 import android.credentials.CredentialOption
@@ -124,13 +122,10 @@
         // TODO(b/324635774): Use callback for validating. If the request is coming
         // directly from the view, there should be a corresponding callback, otherwise
         // we should fail fast,
-        val getCredCallback = getCredManCallback(structure)
         if (getCredRequest == null) {
             Log.i(TAG, "No credential manager request found")
             callback.onFailure("No credential manager request found")
             return
-        } else if (getCredCallback == null) {
-            Log.i(TAG, "No credential manager callback found")
         }
         val credentialManager: CredentialManager =
                 getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
@@ -528,42 +523,6 @@
         TODO("Not yet implemented")
     }
 
-    private fun getCredManCallback(structure: AssistStructure): OutcomeReceiver<
-            GetCredentialResponse, GetCredentialException>? {
-        return traverseStructureForCallback(structure)
-    }
-
-    private fun traverseStructureForCallback(
-            structure: AssistStructure
-    ): OutcomeReceiver<GetCredentialResponse, GetCredentialException>? {
-        val windowNodes: List<AssistStructure.WindowNode> =
-                structure.run {
-                    (0 until windowNodeCount).map { getWindowNodeAt(it) }
-                }
-
-        windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
-            return traverseNodeForCallback(windowNode.rootViewNode)
-        }
-        return null
-    }
-
-    private fun traverseNodeForCallback(
-            viewNode: AssistStructure.ViewNode
-    ): OutcomeReceiver<GetCredentialResponse, GetCredentialException>? {
-        val children: List<AssistStructure.ViewNode> =
-                viewNode.run {
-                    (0 until childCount).map { getChildAt(it) }
-                }
-
-        children.forEach { childNode: AssistStructure.ViewNode ->
-            if (childNode.isFocused() && childNode.credentialManagerCallback != null) {
-                return childNode.credentialManagerCallback
-            }
-            return traverseNodeForCallback(childNode)
-        }
-        return null
-    }
-
     private fun getCredManRequest(
             structure: AssistStructure,
             sessionId: Int,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index a7b5c36..e43b09e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -39,7 +39,6 @@
 import com.android.credentialmanager.ui.theme.EntryShape
 import kotlinx.coroutines.launch
 
-
 /** Draws a modal bottom sheet with the same styles and effects shared by various flows. */
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
@@ -73,7 +72,7 @@
                 dragHandle = null,
                 // Never take over the full screen. We always want to leave some top scrim space
                 // for exiting and viewing the underlying app to help a user gain context.
-                modifier = Modifier.padding(top = 56.dp),
+                modifier = Modifier.padding(top = 72.dp),
         )
     } else {
         val scope = rememberCoroutineScope()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
index c68ae8b..006a2d9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
@@ -16,6 +16,7 @@
 
 package com.android.credentialmanager.common.ui
 
+import android.credentials.flags.Flags
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.WindowInsets
@@ -63,7 +64,7 @@
             modifier = Modifier.padding(
                 start = 24.dp,
                 end = 24.dp,
-                bottom = 18.dp,
+                bottom = if (Flags.selectorUiImprovementsEnabled()) 8.dp else 18.dp,
                 top = if (topAppBar == null) 24.dp else 0.dp
             ).fillMaxWidth().wrapContentHeight(),
             horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 98a5a67..79c810c 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -54,6 +54,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "android.content.pm.flags-aconfig-java",
         "android.os.flags-aconfig-java",
+        "android.multiuser.flags-aconfig-java",
     ],
 
     lint: {
@@ -85,6 +86,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "android.content.pm.flags-aconfig-java",
         "android.os.flags-aconfig-java",
+        "android.multiuser.flags-aconfig-java",
     ],
     aaptflags: ["--product tablet"],
 
@@ -118,6 +120,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "android.content.pm.flags-aconfig-java",
         "android.os.flags-aconfig-java",
+        "android.multiuser.flags-aconfig-java",
     ],
     aaptflags: ["--product tv"],
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 221ca4f..8f5d07c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -166,6 +166,7 @@
                     messageBuilder.append(getString(
                             R.string.uninstall_application_text_current_user_clone_profile));
                 } else if (Flags.allowPrivateProfile()
+                        && android.multiuser.Flags.enablePrivateSpaceFeatures()
                         && customUserManager.isPrivateProfile()
                         && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
                     messageBuilder.append(
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 0fc1845..c6b6d36 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -235,7 +235,9 @@
                     messageString = context.getString(
                             R.string.uninstall_application_text_current_user_clone_profile
                     )
-                } else if (Flags.allowPrivateProfile() && customUserManager!!.isPrivateProfile()) {
+                } else if (Flags.allowPrivateProfile()
+                        && android.multiuser.Flags.enablePrivateSpaceFeatures()
+                        && customUserManager!!.isPrivateProfile()) {
                     // TODO(b/324244123): Get these Strings from a User Property API.
                     messageString = context.getString(
                             R.string.uninstall_application_text_current_user_private_profile
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index fc8de80..0a469b8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -47,7 +47,7 @@
                 onCheckedChange = model.onCheckedChange,
                 paddingStart = 20.dp,
                 paddingEnd = 20.dp,
-                paddingVertical = 18.dp,
+                paddingVertical = 24.dp,
             )
         }
     }
@@ -55,7 +55,7 @@
 
 @Preview
 @Composable
-fun MainSwitchPreferencePreview() {
+private fun MainSwitchPreferencePreview() {
     SettingsTheme {
         Column {
             MainSwitchPreference(object : SwitchPreferenceModel {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index e489bc5..2889ce2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1703,6 +1703,7 @@
 
         public boolean isPrivateProfile() {
             return android.os.Flags.allowPrivateProfile()
+                    && android.multiuser.Flags.enablePrivateSpaceFeatures()
                     && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
         }
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 1ad7d49..fe83ffb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -319,7 +319,8 @@
 
     @Test
     public void testPrivateProfileFilterDisplaysCorrectApps() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
 
         mEntry.showInPersonalTab = true;
         mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
@@ -334,7 +335,8 @@
 
     @Test
     public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
 
         mEntry.showInPersonalTab = false;
         mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0c02f56..eb2d13d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -566,9 +566,6 @@
     <!-- Permission required for CTS test - android.server.biometrics -->
     <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
 
-    <!-- Permission required for CTS test - android.server.biometrics -->
-    <uses-permission android:name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION" />
-
     <!-- Permissions required for CTS test - NotificationManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index d505b27..7fabe33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -25,6 +25,7 @@
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
 import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
+import static android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 import static android.os.UserHandle.USER_ALL;
 import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
@@ -115,7 +116,8 @@
     public static List<FlagsParameterization> getParams() {
         return FlagsParameterization.allCombinationsOf(
                 FLAG_ALLOW_PRIVATE_PROFILE,
-                FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS);
+                FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS,
+                FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
     }
 
     public NotificationLockscreenUserManagerTest(FlagsParameterization flags) {
@@ -872,7 +874,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
+    @EnableFlags({FLAG_ALLOW_PRIVATE_PROFILE, FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
     public void testProfileAvailabilityIntent() {
         mLockscreenUserManager.mCurrentProfiles.clear();
         assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
@@ -883,7 +885,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
+    @EnableFlags({FLAG_ALLOW_PRIVATE_PROFILE, FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
     public void testProfileUnAvailabilityIntent() {
         mLockscreenUserManager.mCurrentProfiles.clear();
         assertEquals(0, mLockscreenUserManager.mCurrentProfiles.size());
@@ -894,7 +896,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
+    @DisableFlags({FLAG_ALLOW_PRIVATE_PROFILE, FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
     public void testManagedProfileAvailabilityIntent() {
         mLockscreenUserManager.mCurrentProfiles.clear();
         mLockscreenUserManager.mCurrentManagedProfiles.clear();
@@ -908,7 +910,7 @@
     }
 
     @Test
-    @DisableFlags(FLAG_ALLOW_PRIVATE_PROFILE)
+    @DisableFlags({FLAG_ALLOW_PRIVATE_PROFILE, FLAG_ENABLE_PRIVATE_SPACE_FEATURES})
     public void testManagedProfileUnAvailabilityIntent() {
         mLockscreenUserManager.mCurrentProfiles.clear();
         mLockscreenUserManager.mCurrentManagedProfiles.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index c0a873a..989b0de 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -18,6 +18,7 @@
 import android.content.Context
 import android.os.Bundle
 import android.view.View
+import android.view.WindowInsets
 import android.widget.TextView
 import androidx.core.view.updatePadding
 import com.android.systemui.res.R
@@ -44,7 +45,10 @@
     private lateinit var mirrorButton: TextView
     private lateinit var dismissButton: TextView
     private lateinit var dualDisplayWarning: TextView
+    private lateinit var bottomSheet: View
     private var enabledPressed = false
+    private val defaultDialogBottomInset =
+        context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -63,6 +67,8 @@
                 visibility = if (showConcurrentDisplayInfo) View.VISIBLE else View.GONE
             }
 
+        bottomSheet = requireViewById(R.id.cd_bottom_sheet)
+
         setOnDismissListener {
             if (!enabledPressed) {
                 onCancelMirroring.onClick(null)
@@ -71,15 +77,17 @@
         setupInsets()
     }
 
-    private fun setupInsets() {
+    private fun setupInsets(navbarInsets: Int = navbarBottomInsetsProvider()) {
         // This avoids overlap between dialog content and navigation bars.
-        requireViewById<View>(R.id.cd_bottom_sheet).apply {
-            val navbarInsets = navbarBottomInsetsProvider()
-            val defaultDialogBottomInset =
-                context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
-            // we only care about the bottom inset as in all other configuration where navigations
-            // are in other display sides there is no overlap with the dialog.
-            updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
+        // we only care about the bottom inset as in all other configuration where navigations
+        // are in other display sides there is no overlap with the dialog.
+        bottomSheet.updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
+    }
+
+    override fun onInsetsChanged(changedTypes: Int, insets: WindowInsets) {
+        val navbarType = WindowInsets.Type.navigationBars()
+        if (changedTypes and navbarType != 0) {
+            setupInsets(insets.getInsets(navbarType).bottom)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 190062c..fbf0538 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -32,9 +32,12 @@
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.launch
@@ -57,6 +60,7 @@
     private var dialog: Dialog? = null
 
     /** Starts listening for pending displays. */
+    @OptIn(FlowPreview::class)
     override fun start() {
         val pendingDisplayFlow = connectedDisplayInteractor.pendingDisplay
         val concurrentDisplaysInProgessFlow =
@@ -66,6 +70,13 @@
                 flow { emit(false) }
             }
         pendingDisplayFlow
+            // Let's debounce for 2 reasons:
+            // - prevent fast dialog flashes in case pending displays are available for just a few
+            // millis
+            // - Prevent jumps related to inset changes: when in 3 buttons navigation, device
+            // unlock triggers a change in insets that might result in a jump of the dialog (if a
+            // display was connected while on the lockscreen).
+            .debounce(200.milliseconds)
             .combine(concurrentDisplaysInProgessFlow) { pendingDisplay, concurrentDisplaysInProgress
                 ->
                 if (pendingDisplay == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index bcad332..12b27eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -357,7 +357,8 @@
                 from = KeyguardState.LOCKSCREEN,
                 modeOnCanceledFromStartedStep = { startedStep ->
                     if (
-                        startedStep.to == KeyguardState.AOD && startedStep.from == KeyguardState.AOD
+                        transitionInteractor.asleepKeyguardState.value == KeyguardState.AOD &&
+                            startedStep.from == KeyguardState.AOD
                     ) {
                         TransitionModeOnCanceled.REVERSE
                     } else {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index c0e688f..c7aae3c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -23,6 +23,7 @@
 import android.app.role.RoleManager
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesService
 import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule
 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
 import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
@@ -37,20 +38,21 @@
 interface NoteTaskModule {
 
     @[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)]
-    fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service
+    fun bindNoteTaskControllerUpdateService(service: NoteTaskControllerUpdateService): Service
 
-    @[Binds IntoMap ClassKey(NoteTaskBubblesController.NoteTaskBubblesService::class)]
-    fun NoteTaskBubblesController.NoteTaskBubblesService.bindNoteTaskBubblesService(): Service
+    @[Binds IntoMap ClassKey(NoteTaskBubblesService::class)]
+    fun bindNoteTaskBubblesService(service: NoteTaskBubblesService): Service
 
     @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
-    fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
+    fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity
 
     @[Binds IntoMap ClassKey(LaunchNotesRoleSettingsTrampolineActivity::class)]
-    fun LaunchNotesRoleSettingsTrampolineActivity.bindLaunchNotesRoleSettingsTrampolineActivity():
-        Activity
+    fun bindLaunchNotesRoleSettingsTrampolineActivity(
+        activity: LaunchNotesRoleSettingsTrampolineActivity
+    ): Activity
 
     @[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
-    fun CreateNoteTaskShortcutActivity.bindNoteTaskShortcutActivity(): Activity
+    fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity
 
     companion object {
 
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
index 2d63dbc..7d3f2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt
@@ -25,5 +25,7 @@
 interface NoteTaskQuickAffordanceModule {
 
     @[Binds IntoSet]
-    fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig
+    fun bindNoteTaskQuickAffordance(
+        impl: NoteTaskQuickAffordanceConfig
+    ): KeyguardQuickAffordanceConfig
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 1a06eec..0091bc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -420,7 +420,7 @@
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-        if (allowPrivateProfile()){
+        if (privateSpaceFlagsEnabled()) {
             filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
             filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
         }
@@ -813,13 +813,17 @@
     }
 
     private boolean profileAvailabilityActions(String action){
-        return allowPrivateProfile()?
+        return privateSpaceFlagsEnabled()?
                 Objects.equals(action,Intent.ACTION_PROFILE_AVAILABLE)||
                         Objects.equals(action,Intent.ACTION_PROFILE_UNAVAILABLE):
                 Objects.equals(action,Intent.ACTION_MANAGED_PROFILE_AVAILABLE)||
                         Objects.equals(action,Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
     }
 
+    private static boolean privateSpaceFlagsEnabled() {
+        return allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures();
+    }
+
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("NotificationLockscreenUserManager state:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index bfda6d5..b47b18d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
@@ -3306,7 +3308,11 @@
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (SceneContainerFlag.isEnabled() && mIsBeingDragged) {
-            if (!mSendingTouchesToSceneFramework) {
+            int action = ev.getActionMasked();
+            boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
+            if (mSendingTouchesToSceneFramework) {
+                mController.sendTouchToSceneFramework(ev);
+            } else if (!isUpOrCancel) {
                 // if this is the first touch being sent to the scene framework,
                 // convert it into a synthetic DOWN event.
                 mSendingTouchesToSceneFramework = true;
@@ -3314,14 +3320,9 @@
                 downEvent.setAction(MotionEvent.ACTION_DOWN);
                 mController.sendTouchToSceneFramework(downEvent);
                 downEvent.recycle();
-            } else {
-                mController.sendTouchToSceneFramework(ev);
             }
 
-            if (
-                    ev.getActionMasked() == MotionEvent.ACTION_UP
-                    || ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-            ) {
+            if (isUpOrCancel) {
                 setIsBeingDragged(false);
             }
             return false;
@@ -3478,7 +3479,7 @@
                     }
                 }
                 break;
-            case MotionEvent.ACTION_UP:
+            case ACTION_UP:
                 if (mIsBeingDragged) {
                     final VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
@@ -3515,7 +3516,7 @@
                 }
 
                 break;
-            case MotionEvent.ACTION_CANCEL:
+            case ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
                     if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
                             getScrollRange())) {
@@ -3624,7 +3625,7 @@
                     mTouchIsClick = false;
                 }
                 break;
-            case MotionEvent.ACTION_UP:
+            case ACTION_UP:
                 if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
                         isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
                     debugShadeLog("handleEmptySpaceClick: touch event propagated further");
@@ -3765,8 +3766,8 @@
                 break;
             }
 
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
+            case ACTION_CANCEL:
+            case ACTION_UP:
                 /* Release the drag */
                 setIsBeingDragged(false);
                 mActivePointerId = INVALID_POINTER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
index 71e25e9..541ac48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
@@ -23,6 +23,9 @@
 import android.os.Bundle
 import android.view.Gravity
 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowInsets
+import android.view.WindowInsets.Type.InsetsType
+import android.view.WindowInsetsAnimation
 import android.view.WindowManager.LayoutParams.MATCH_PARENT
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
 import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
@@ -70,16 +73,43 @@
     override fun onStart() {
         super.onStart()
         configurationController?.addCallback(onConfigChanged)
+        window?.decorView?.setWindowInsetsAnimationCallback(insetsAnimationCallback)
     }
 
     override fun onStop() {
         super.onStop()
         configurationController?.removeCallback(onConfigChanged)
+        window?.decorView?.setWindowInsetsAnimationCallback(null)
     }
 
+    /** Called after any insets change. */
+    open fun onInsetsChanged(@InsetsType changedTypes: Int, insets: WindowInsets) {}
+
     /** Can be overridden by subclasses to receive config changed events. */
     open fun onConfigurationChanged() {}
 
+    private val insetsAnimationCallback =
+        object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+
+            private var lastInsets: WindowInsets? = null
+
+            override fun onEnd(animation: WindowInsetsAnimation) {
+                lastInsets?.let { onInsetsChanged(animation.typeMask, it) }
+            }
+
+            override fun onProgress(
+                insets: WindowInsets,
+                animations: MutableList<WindowInsetsAnimation>,
+            ): WindowInsets {
+                lastInsets = insets
+                onInsetsChanged(changedTypes = allAnimationMasks(animations), insets)
+                return insets
+            }
+
+            private fun allAnimationMasks(animations: List<WindowInsetsAnimation>): Int =
+                animations.fold(0) { acc: Int, it -> acc or it.typeMask }
+        }
+
     private val onConfigChanged =
         object : ConfigurationListener {
             override fun onConfigChanged(newConfig: Configuration?) {
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 44c684c..b5efc44 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -379,7 +379,9 @@
                             + " was received. Deferring... Managed profile? " + isManagedProfile);
                     return;
                 }
-                if (android.os.Flags.allowPrivateProfile() && isPrivateProfile(newUserHandle)) {
+                if (android.os.Flags.allowPrivateProfile()
+                        && android.multiuser.Flags.enablePrivateSpaceFeatures()
+                        && isPrivateProfile(newUserHandle)) {
                     mDeferredThemeEvaluation = true;
                     Log.i(TAG, "Deferring theme for private profile till user setup is complete");
                     return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
index b25fb6e..30519b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
@@ -16,14 +16,17 @@
 
 package com.android.systemui.display.ui.view
 
+import android.graphics.Insets
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
+import android.view.WindowInsets
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -41,6 +44,7 @@
 
     private val onStartMirroringCallback = mock<View.OnClickListener>()
     private val onCancelCallback = mock<View.OnClickListener>()
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -96,10 +100,40 @@
         verify(onStartMirroringCallback).onClick(any())
     }
 
+    @Test
+    fun onInsetsChanged_navBarInsets_updatesBottomPadding() {
+        dialog.show()
+
+        val insets = buildInsets(WindowInsets.Type.navigationBars(), TEST_BOTTOM_INSETS)
+        dialog.onInsetsChanged(WindowInsets.Type.navigationBars(), insets)
+
+        assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
+            .isEqualTo(TEST_BOTTOM_INSETS)
+    }
+
+    @Test
+    fun onInsetsChanged_otherType_doesNotUpdateBottomPadding() {
+        dialog.show()
+
+        val insets = buildInsets(WindowInsets.Type.ime(), TEST_BOTTOM_INSETS)
+        dialog.onInsetsChanged(WindowInsets.Type.ime(), insets)
+
+        assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
+            .isNotEqualTo(TEST_BOTTOM_INSETS)
+    }
+
+    private fun buildInsets(@WindowInsets.Type.InsetsType type: Int, bottom: Int): WindowInsets {
+        return WindowInsets.Builder().setInsets(type, Insets.of(0, 0, 0, bottom)).build()
+    }
+
     @After
     fun teardown() {
         if (::dialog.isInitialized) {
             dialog.dismiss()
         }
     }
+
+    private companion object {
+        const val TEST_BOTTOM_INSETS = 1000 // arbitrarily high number
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 28dfbbb..220305c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -38,7 +38,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
@@ -73,11 +72,11 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -97,13 +96,11 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -232,6 +229,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
     public void testUpdateStackHeight_qsExpansionZero() {
         final float expansionFraction = 0.2f;
         final float overExpansion = 50f;
@@ -730,6 +728,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address lack of QS Header
     public void testInsideQSHeader_noOffset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -746,6 +745,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address lack of QS Header
     public void testInsideQSHeader_Offset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -765,12 +765,14 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
     public void setFractionToShade_recomputesStackHeight() {
         mStackScroller.setFractionToShade(1f);
         verify(mNotificationStackSizeCalculator).computeHeight(any(), anyInt(), anyFloat());
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
     public void testSetOwnScrollY_shadeNotClosing_scrollYChanges() {
         // Given: shade is not closing, scrollY is 0
         mAmbientState.setScrollY(0);
@@ -869,6 +871,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
     public void testSplitShade_hasTopOverscroll() {
         mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
@@ -941,6 +944,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER) // TODO(b/312473478): address disabled test
     public void testSetMaxDisplayedNotifications_notifiesListeners() {
         ExpandableView.OnHeightChangedListener listener =
                 mock(ExpandableView.OnHeightChangedListener.class);
@@ -955,9 +959,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_SCENE_CONTAINER)
     public void testDispatchTouchEvent_sceneContainerDisabled() {
-        Assume.assumeFalse(SceneContainerFlag.isEnabled());
-
         MotionEvent event = MotionEvent.obtain(
                 SystemClock.uptimeMillis(),
                 SystemClock.uptimeMillis(),
@@ -973,34 +976,60 @@
     }
 
     @Test
+    @EnableSceneContainer
     public void testDispatchTouchEvent_sceneContainerEnabled() {
-        Assume.assumeTrue(SceneContainerFlag.isEnabled());
         mStackScroller.setIsBeingDragged(true);
 
-        MotionEvent moveEvent = MotionEvent.obtain(
-                SystemClock.uptimeMillis(),
-                SystemClock.uptimeMillis(),
+        long downTime = SystemClock.uptimeMillis() - 100;
+        MotionEvent moveEvent1 = MotionEvent.obtain(
+                /* downTime= */ downTime,
+                /* eventTime= */ SystemClock.uptimeMillis(),
                 MotionEvent.ACTION_MOVE,
-                0,
-                0,
+                101,
+                201,
                 0
         );
-        MotionEvent syntheticDownEvent = moveEvent.copy();
+        MotionEvent syntheticDownEvent = moveEvent1.copy();
         syntheticDownEvent.setAction(MotionEvent.ACTION_DOWN);
-        mStackScroller.dispatchTouchEvent(moveEvent);
+        mStackScroller.dispatchTouchEvent(moveEvent1);
 
-        verify(mStackScrollLayoutController).sendTouchToSceneFramework(argThat(
-                new MotionEventMatcher(syntheticDownEvent)));
+        assertThatMotionEvent(captureTouchSentToSceneFramework()).matches(syntheticDownEvent);
+        assertTrue(mStackScroller.getIsBeingDragged());
+        clearInvocations(mStackScrollLayoutController);
 
-        mStackScroller.dispatchTouchEvent(moveEvent);
+        MotionEvent moveEvent2 = MotionEvent.obtain(
+                /* downTime= */ downTime,
+                /* eventTime= */ SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_MOVE,
+                102,
+                202,
+                0
+        );
 
-        verify(mStackScrollLayoutController).sendTouchToSceneFramework(moveEvent);
+        mStackScroller.dispatchTouchEvent(moveEvent2);
+
+        assertThatMotionEvent(captureTouchSentToSceneFramework()).matches(moveEvent2);
+        assertTrue(mStackScroller.getIsBeingDragged());
+        clearInvocations(mStackScrollLayoutController);
+
+        MotionEvent upEvent = MotionEvent.obtain(
+                /* downTime= */ downTime,
+                /* eventTime= */ SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP,
+                103,
+                203,
+                0
+        );
+
+        mStackScroller.dispatchTouchEvent(upEvent);
+
+        assertThatMotionEvent(captureTouchSentToSceneFramework()).matches(upEvent);
+        assertFalse(mStackScroller.getIsBeingDragged());
     }
 
     @Test
-    @EnableFlags(FLAG_SCENE_CONTAINER)
-    public void testDispatchTouchEvent_sceneContainerEnabled_actionUp() {
-        Assume.assumeTrue(SceneContainerFlag.isEnabled());
+    @EnableSceneContainer
+    public void testDispatchTouchEvent_sceneContainerEnabled_ignoresInitialActionUp() {
         mStackScroller.setIsBeingDragged(true);
 
         MotionEvent upEvent = MotionEvent.obtain(
@@ -1011,21 +1040,18 @@
                 0,
                 0
         );
-        MotionEvent syntheticDownEvent = upEvent.copy();
-        syntheticDownEvent.setAction(MotionEvent.ACTION_DOWN);
 
         mStackScroller.dispatchTouchEvent(upEvent);
-
-        verify(mStackScrollLayoutController, atLeastOnce()).sendTouchToSceneFramework(argThat(
-                new MotionEventMatcher(syntheticDownEvent)));
-
-        mStackScroller.dispatchTouchEvent(upEvent);
-
-        verify(mStackScrollLayoutController, atLeastOnce()).sendTouchToSceneFramework(argThat(
-                new MotionEventMatcher(upEvent)));
+        verify(mStackScrollLayoutController, never()).sendTouchToSceneFramework(any());
         assertFalse(mStackScroller.getIsBeingDragged());
     }
 
+    private MotionEvent captureTouchSentToSceneFramework() {
+        ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class);
+        verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture());
+        return captor.getValue();
+    }
+
     private void setBarStateForTest(int state) {
         // Can't inject this through the listener or we end up on the actual implementation
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
@@ -1073,20 +1099,23 @@
         );
     }
 
-    private static class MotionEventMatcher implements ArgumentMatcher<MotionEvent> {
-        private final MotionEvent mLeftEvent;
+    private MotionEventSubject assertThatMotionEvent(MotionEvent actual) {
+        return new MotionEventSubject(actual);
+    }
 
-        MotionEventMatcher(MotionEvent leftEvent) {
-            mLeftEvent = leftEvent;
+    private static class MotionEventSubject {
+        private final MotionEvent mActual;
+
+        MotionEventSubject(MotionEvent actual) {
+            mActual = actual;
         }
 
-        @Override
-        public boolean matches(MotionEvent right) {
-            return mLeftEvent.getActionMasked() == right.getActionMasked()
-                    && mLeftEvent.getDownTime() == right.getDownTime()
-                    && mLeftEvent.getEventTime() == right.getEventTime()
-                    && mLeftEvent.getX() == right.getX()
-                    && mLeftEvent.getY() == right.getY();
+        public void matches(MotionEvent expected) {
+            assertThat(mActual.getActionMasked()).isEqualTo(expected.getActionMasked());
+            assertThat(mActual.getDownTime()).isEqualTo(expected.getDownTime());
+            assertThat(mActual.getEventTime()).isEqualTo(expected.getEventTime());
+            assertThat(mActual.getX()).isEqualTo(expected.getX());
+            assertThat(mActual.getY()).isEqualTo(expected.getY());
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index c02583a..ab28a2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -718,7 +718,8 @@
 
     @Test
     public void onPrivateProfileAdded_ignoresUntilStartComplete() {
-        mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         reset(mDeviceProvisionedController);
         when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
         mBroadcastReceiver.getValue().onReceive(null,
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index f31eb44..23e269a 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -39,7 +39,9 @@
 import android.content.SharedPreferences;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -49,16 +51,22 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
+import android.view.Display;
+import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -102,6 +110,9 @@
     @VisibleForTesting
     static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage";
 
+    @VisibleForTesting
+    static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage";
+
     static final String EMPTY_SENTINEL = "empty";
     static final String QUOTA_SENTINEL = "quota";
 
@@ -110,6 +121,11 @@
     static final String SYSTEM_GENERATION = "system_gen";
     static final String LOCK_GENERATION = "lock_gen";
 
+    /**
+     * An approximate area threshold to compare device dimension similarity
+     */
+    static final int AREA_THRESHOLD = 50; // TODO (b/327637867): determine appropriate threshold
+
     // If this file exists, it means we exceeded our quota last time
     private File mQuotaFile;
     private boolean mQuotaExceeded;
@@ -121,6 +137,8 @@
     private boolean mSystemHasLiveComponent;
     private boolean mLockHasLiveComponent;
 
+    private DisplayManager mDisplayManager;
+
     @Override
     public void onCreate() {
         if (DEBUG) {
@@ -137,6 +155,8 @@
 
         mBackupManager = new BackupManager(getBaseContext());
         mEventLogger = new WallpaperEventLogger(mBackupManager, /* wallpaperAgent */ this);
+
+        mDisplayManager = getSystemService(DisplayManager.class);
     }
 
     @Override
@@ -175,9 +195,11 @@
             mSystemHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM) != null;
             mLockHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_LOCK) != null;
 
+            // performing backup of each file based on order of importance
             backupWallpaperInfoFile(/* sysOrLockChanged= */ sysChanged || lockChanged, data);
             backupSystemWallpaperFile(sharedPrefs, sysChanged, sysGeneration, data);
             backupLockWallpaperFileIfItExists(sharedPrefs, lockChanged, lockGeneration, data);
+            backupDeviceInfoFile(data);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to back up wallpaper", e);
             mEventLogger.onBackupException(e);
@@ -191,6 +213,54 @@
         }
     }
 
+    /**
+     * This method backs up the device dimension information. The device data will always get
+     * overwritten when triggering a backup
+     */
+    private void backupDeviceInfoFile(FullBackupDataOutput data)
+            throws IOException {
+        final File deviceInfoStage = new File(getFilesDir(), WALLPAPER_BACKUP_DEVICE_INFO_STAGE);
+
+        // save the dimensions of the device with xml formatting
+        Point dimensions = getScreenDimensions();
+        Display smallerDisplay = getSmallerDisplayIfExists();
+        Point secondaryDimensions = smallerDisplay != null ? getRealSize(smallerDisplay) :
+                new Point(0, 0);
+
+        deviceInfoStage.createNewFile();
+        FileOutputStream fstream = new FileOutputStream(deviceInfoStage, false);
+        TypedXmlSerializer out = Xml.resolveSerializer(fstream);
+        out.startDocument(null, true);
+        out.startTag(null, "dimensions");
+
+        out.startTag(null, "width");
+        out.text(String.valueOf(dimensions.x));
+        out.endTag(null, "width");
+
+        out.startTag(null, "height");
+        out.text(String.valueOf(dimensions.y));
+        out.endTag(null, "height");
+
+        if (smallerDisplay != null) {
+            out.startTag(null, "secondarywidth");
+            out.text(String.valueOf(secondaryDimensions.x));
+            out.endTag(null, "secondarywidth");
+
+            out.startTag(null, "secondaryheight");
+            out.text(String.valueOf(secondaryDimensions.y));
+            out.endTag(null, "secondaryheight");
+        }
+
+        out.endTag(null, "dimensions");
+        out.endDocument();
+        fstream.flush();
+        FileUtils.sync(fstream);
+        fstream.close();
+
+        if (DEBUG) Slog.v(TAG, "Storing device dimension data");
+        backupFile(deviceInfoStage, data);
+    }
+
     private void backupWallpaperInfoFile(boolean sysOrLockChanged, FullBackupDataOutput data)
             throws IOException {
         final ParcelFileDescriptor wallpaperInfoFd = mWallpaperManager.getWallpaperInfoFile();
@@ -364,9 +434,22 @@
         final File infoStage = new File(filesDir, WALLPAPER_INFO_STAGE);
         final File imageStage = new File(filesDir, SYSTEM_WALLPAPER_STAGE);
         final File lockImageStage = new File(filesDir, LOCK_WALLPAPER_STAGE);
+        final File deviceDimensionsStage = new File(filesDir, WALLPAPER_BACKUP_DEVICE_INFO_STAGE);
         boolean lockImageStageExists = lockImageStage.exists();
 
         try {
+            // Parse the device dimensions of the source device and compare with target to
+            // to identify whether we need to skip the remainder of the restore process
+            Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions(
+                    deviceDimensionsStage);
+
+            Point targetDeviceDimensions = getScreenDimensions();
+            if (sourceDeviceDimensions != null && targetDeviceDimensions != null
+                    && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first,
+                    targetDeviceDimensions)) {
+                Slog.d(TAG, "The source device is significantly smaller than target");
+            }
+
             // First parse the live component name so that we know for logging if we care about
             // logging errors with the image restore.
             ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
@@ -400,6 +483,7 @@
             infoStage.delete();
             imageStage.delete();
             lockImageStage.delete();
+            deviceDimensionsStage.delete();
 
             SharedPreferences prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
             prefs.edit()
@@ -409,6 +493,66 @@
         }
     }
 
+    /**
+     * This method parses the given file for the backed up device dimensions
+     *
+     * @param deviceDimensions the file which holds the device dimensions
+     * @return the backed up device dimensions
+     */
+    private Pair<Point, Point> parseDeviceDimensions(File deviceDimensions) {
+        int width = 0, height = 0, secondaryHeight = 0, secondaryWidth = 0;
+        try {
+            TypedXmlPullParser parser = Xml.resolvePullParser(
+                    new FileInputStream(deviceDimensions));
+
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                String name = parser.getName();
+
+                switch (name) {
+                    case "width":
+                        String widthText = readText(parser);
+                        width = Integer.valueOf(widthText);
+                        break;
+
+                    case "height":
+                        String textHeight = readText(parser);
+                        height = Integer.valueOf(textHeight);
+                        break;
+
+                    case "secondarywidth":
+                        String secondaryWidthText = readText(parser);
+                        secondaryWidth = Integer.valueOf(secondaryWidthText);
+                        break;
+
+                    case "secondaryheight":
+                        String secondaryHeightText = readText(parser);
+                        secondaryHeight = Integer.valueOf(secondaryHeightText);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            return new Pair<>(new Point(width, height), new Point(secondaryWidth, secondaryHeight));
+
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private static String readText(TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String result = "";
+        if (parser.next() == XmlPullParser.TEXT) {
+            result = parser.getText();
+            parser.nextTag();
+        }
+        return result;
+    }
+
     @VisibleForTesting
     void updateWallpaperComponent(ComponentName wpService, int which)
             throws IOException {
@@ -691,6 +835,94 @@
         };
     }
 
+    /**
+     * This method retrieves the dimensions of the largest display of the device
+     *
+     * @return a @{Point} object that contains the dimensions of the largest display on the device
+     */
+    private Point getScreenDimensions() {
+        Point largetDimensions = null;
+        int maxArea = 0;
+
+        for (Display display : getInternalDisplays()) {
+            Point displaySize = getRealSize(display);
+
+            int width = displaySize.x;
+            int height = displaySize.y;
+            int area = width * height;
+
+            if (area > maxArea) {
+                maxArea = area;
+                largetDimensions = displaySize;
+            }
+        }
+
+        return largetDimensions;
+    }
+
+    private Point getRealSize(Display display) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        display.getDisplayInfo(displayInfo);
+        return new Point(displayInfo.logicalWidth, displayInfo.logicalHeight);
+    }
+
+    /**
+     * This method returns the smaller display on a multi-display device
+     *
+     * @return Display that corresponds to the smaller display on a device or null if ther is only
+     * one Display on a device
+     */
+    private Display getSmallerDisplayIfExists() {
+        List<Display> internalDisplays = getInternalDisplays();
+        Point largestDisplaySize = getScreenDimensions();
+
+        // Find the first non-matching internal display
+        for (Display display : internalDisplays) {
+            Point displaySize = getRealSize(display);
+            if (displaySize.x != largestDisplaySize.x || displaySize.y != largestDisplaySize.y) {
+                return display;
+            }
+        }
+
+        // If no smaller display found, return null, as there is only a single display
+        return null;
+    }
+
+    /**
+     * This method retrieves the collection of Display objects available in the device.
+     * i.e. non-external displays are ignored
+     *
+     * @return list of displays corresponding to each display in the device
+     */
+    private List<Display> getInternalDisplays() {
+        Display[] allDisplays = mDisplayManager.getDisplays(
+                DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+
+        List<Display> internalDisplays = new ArrayList<>();
+        for (Display display : allDisplays) {
+            if (display.getType() == Display.TYPE_INTERNAL) {
+                internalDisplays.add(display);
+            }
+        }
+        return internalDisplays;
+    }
+
+    /**
+     * This method compares the source and target dimensions, and returns true if there is a
+     * significant difference in area between them and the source dimensions are smaller than the
+     * target dimensions.
+     *
+     * @param sourceDimensions is the dimensions of the source device
+     * @param targetDimensions is the dimensions of the target device
+     */
+    @VisibleForTesting
+    boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions,
+            Point targetDimensions) {
+        int rawAreaDelta = (targetDimensions.x * targetDimensions.y)
+                - (sourceDimensions.x * sourceDimensions.y);
+        return rawAreaDelta > AREA_THRESHOLD;
+    }
+
     @VisibleForTesting
     boolean isDeviceInRestore() {
         try {
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 3ecdf3f..ec9223c 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -59,6 +59,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
@@ -840,6 +841,26 @@
         testParseCropHints(testMap);
     }
 
+    @Test
+    public void test_sourceDimensionsAreLargerThanTarget() {
+        // source device is larger than target, expecting to get false
+        Point sourceDimensions = new Point(2208, 1840);
+        Point targetDimensions = new Point(1080, 2092);
+        boolean isSourceSmaller = mWallpaperBackupAgent
+                .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
+        assertThat(isSourceSmaller).isEqualTo(false);
+    }
+
+    @Test
+    public void test_sourceDimensionsMuchSmallerThanTarget() {
+        // source device is smaller than target, expecting to get true
+        Point sourceDimensions = new Point(1080, 2092);
+        Point targetDimensions = new Point(2208, 1840);
+        boolean isSourceSmaller = mWallpaperBackupAgent
+                .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
+        assertThat(isSourceSmaller).isEqualTo(true);
+    }
+
     private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
         assumeTrue(multiCrop());
         mockRestoredStaticWallpaperFile(testMap);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 29b9d44..1749ee3 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -84,6 +84,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -98,6 +99,7 @@
 import android.service.appwidget.AppWidgetServiceDumpProto;
 import android.service.appwidget.WidgetProto;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
@@ -148,6 +150,7 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -159,6 +162,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.LongSupplier;
 
 class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
         OnCrossProfileWidgetProvidersChangeListener {
@@ -187,6 +191,13 @@
     // used to verify which request has successfully been received by the host.
     private static final AtomicLong UPDATE_COUNTER = new AtomicLong();
 
+    // Default reset interval for generated preview API rate limiting.
+    private static final long DEFAULT_GENERATED_PREVIEW_RESET_INTERVAL_MS =
+            Duration.ofHours(1).toMillis();
+    // Default max API calls per reset interval for generated preview API rate limiting.
+    private static final int DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL = 2;
+
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -266,6 +277,8 @@
     // Mark widget lifecycle broadcasts as 'interactive'
     private Bundle mInteractiveBroadcast;
 
+    private ApiCounter mGeneratedPreviewsApiCounter;
+
     AppWidgetServiceImpl(Context context) {
         mContext = context;
     }
@@ -294,6 +307,17 @@
         mIsCombinedBroadcastEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
             SystemUiDeviceConfigFlags.COMBINED_BROADCAST_ENABLED, true);
 
+        final long generatedPreviewResetInterval = DeviceConfig.getLong(NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
+                DEFAULT_GENERATED_PREVIEW_RESET_INTERVAL_MS);
+        final int generatedPreviewMaxCallsPerInterval = DeviceConfig.getInt(NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
+                DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL);
+        mGeneratedPreviewsApiCounter = new ApiCounter(generatedPreviewResetInterval,
+                generatedPreviewMaxCallsPerInterval);
+        DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
+                new HandlerExecutor(mCallbackHandler), this::handleSystemUiDeviceConfigChange);
+
         BroadcastOptions opts = BroadcastOptions.makeBasic();
         opts.setBackgroundActivityStartsAllowed(false);
         opts.setInteractive(true);
@@ -2426,7 +2450,8 @@
         AppWidgetProviderInfo info = createPartialProviderInfo(providerId, ri, existing);
 
         if (android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.disablePrivateSpaceItemsOnHome()) {
+                && android.multiuser.Flags.disablePrivateSpaceItemsOnHome()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             // Do not add widget providers for profiles with items restricted on home screen.
             if (info != null && mUserManager
                     .getUserProperties(info.getProfile()).areItemsRestrictedOnHomeScreen()) {
@@ -2480,6 +2505,7 @@
     private void deleteProviderLocked(Provider provider) {
         deleteWidgetsLocked(provider, UserHandle.USER_ALL);
         mProviders.remove(provider);
+        mGeneratedPreviewsApiCounter.remove(provider.id);
 
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
         cancelBroadcastsLocked(provider);
@@ -4004,7 +4030,7 @@
     }
 
     @Override
-    public void setWidgetPreview(@NonNull ComponentName providerComponent,
+    public boolean setWidgetPreview(@NonNull ComponentName providerComponent,
             @AppWidgetProviderInfo.CategoryFlags int widgetCategories,
             @NonNull RemoteViews preview) {
         final int userId = UserHandle.getCallingUserId();
@@ -4026,8 +4052,12 @@
                 throw new IllegalArgumentException(
                         providerComponent + " is not a valid AppWidget provider");
             }
-            provider.setGeneratedPreviewLocked(widgetCategories, preview);
-            scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+            if (mGeneratedPreviewsApiCounter.tryApiCall(providerId)) {
+                provider.setGeneratedPreviewLocked(widgetCategories, preview);
+                scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+                return true;
+            }
+            return false;
         }
     }
 
@@ -4068,6 +4098,26 @@
         }
     }
 
+    private void handleSystemUiDeviceConfigChange(DeviceConfig.Properties properties) {
+        Set<String> changed = properties.getKeyset();
+        synchronized (mLock) {
+            if (changed.contains(
+                    SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS)) {
+                long resetIntervalMs = properties.getLong(
+                        SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
+                        /* defaultValue= */ mGeneratedPreviewsApiCounter.getResetIntervalMs());
+                mGeneratedPreviewsApiCounter.setResetIntervalMs(resetIntervalMs);
+            }
+            if (changed.contains(
+                    SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL)) {
+                int maxCallsPerInterval = properties.getInt(
+                        SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL,
+                        /* defaultValue= */ mGeneratedPreviewsApiCounter.getMaxCallsPerInterval());
+                mGeneratedPreviewsApiCounter.setMaxCallsPerInterval(maxCallsPerInterval);
+            }
+        }
+    }
+
     private final class CallbackHandler extends Handler {
         public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
         public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
@@ -4541,11 +4591,11 @@
         }
     }
 
-    private static final class ProviderId {
+    static final class ProviderId {
         final int uid;
         final ComponentName componentName;
 
-        private ProviderId(int uid, ComponentName componentName) {
+        ProviderId(int uid, ComponentName componentName) {
             this.uid = uid;
             this.componentName = componentName;
         }
@@ -4788,6 +4838,96 @@
         }
     }
 
+    /**
+     * This class keeps track of API calls and implements rate limiting. One instance of this class
+     * tracks calls from all providers for one API, or a group of APIs that should share the same
+     * rate limit.
+     */
+    static final class ApiCounter {
+
+        private static final class ApiCallRecord {
+            // Number of times the API has been called for this provider.
+            public int apiCallCount = 0;
+            // The last time (from SystemClock.elapsedRealtime) the api call count was reset.
+            public long lastResetTimeMs = 0;
+
+            void reset(long nowMs) {
+                apiCallCount = 0;
+                lastResetTimeMs = nowMs;
+            }
+        }
+
+        private final Map<ProviderId, ApiCallRecord> mCallCount = new ArrayMap<>();
+        // The interval at which the call count is reset.
+        private long mResetIntervalMs;
+        // The max number of API calls per interval.
+        private int mMaxCallsPerInterval;
+        // Returns the current time (monotonic). By default this is SystemClock.elapsedRealtime.
+        private LongSupplier mMonotonicClock;
+
+        ApiCounter(long resetIntervalMs, int maxCallsPerInterval) {
+            this(resetIntervalMs, maxCallsPerInterval, SystemClock::elapsedRealtime);
+        }
+
+        ApiCounter(long resetIntervalMs, int maxCallsPerInterval,
+                LongSupplier monotonicClock) {
+            mResetIntervalMs = resetIntervalMs;
+            mMaxCallsPerInterval = maxCallsPerInterval;
+            mMonotonicClock = monotonicClock;
+        }
+
+        public void setResetIntervalMs(long resetIntervalMs) {
+            mResetIntervalMs = resetIntervalMs;
+        }
+
+        public long getResetIntervalMs() {
+            return mResetIntervalMs;
+        }
+
+        public void setMaxCallsPerInterval(int maxCallsPerInterval) {
+            mMaxCallsPerInterval = maxCallsPerInterval;
+        }
+
+        public int getMaxCallsPerInterval() {
+            return mMaxCallsPerInterval;
+        }
+
+        /**
+         * Returns true if the API call for the provider should be allowed, false if it should be
+         * rate-limited.
+         */
+        public boolean tryApiCall(@NonNull ProviderId provider) {
+            final ApiCallRecord record = getOrCreateRecord(provider);
+            final long now = mMonotonicClock.getAsLong();
+            final long timeSinceLastResetMs = now - record.lastResetTimeMs;
+            // If the last reset was beyond the reset interval, reset now.
+            if (timeSinceLastResetMs > mResetIntervalMs) {
+                record.reset(now);
+            }
+            if (record.apiCallCount < mMaxCallsPerInterval) {
+                record.apiCallCount++;
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Remove the provider's call record from this counter, when the provider is no longer
+         * tracked.
+         */
+        public void remove(@NonNull ProviderId id) {
+            mCallCount.remove(id);
+        }
+
+        @NonNull
+        private ApiCallRecord getOrCreateRecord(@NonNull ProviderId provider) {
+            if (!mCallCount.containsKey(provider)) {
+                mCallCount.put(provider, new ApiCallRecord());
+            }
+            return mCallCount.get(provider);
+        }
+    }
+
     private class LoadedWidgetState {
         final Widget widget;
         final int hostTag;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 551297b..7afb780 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3935,6 +3935,24 @@
      */
     @GuardedBy("mLock")
     @Nullable
+    private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) {
+        final int numContexts = mContexts.size();
+        for (int i = numContexts - 1; i >= 0; i--) {
+            final FillContext context = mContexts.get(i);
+            final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
+                    autofillId);
+            if (node != null) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets the latest non-empty value for the given id in the autofill contexts.
+     */
+    @GuardedBy("mLock")
+    @Nullable
     private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) {
         final int numContexts = mContexts.size();
         for (int i = numContexts - 1; i >= 0; i--) {
@@ -6417,7 +6435,21 @@
                     mClient.onGetCredentialException(id, viewId, exception.getType(),
                             exception.getMessage());
                 } else if (response != null) {
-                    mClient.onGetCredentialResponse(id, viewId, response);
+                    if (viewId.isVirtualInt()) {
+                        ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
+                        if (viewNode != null && viewNode.getCredentialManagerCallback() != null) {
+                            Bundle resultData = new Bundle();
+                            resultData.putParcelable(
+                                    CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+                                    response);
+                            viewNode.getCredentialManagerCallback().send(SUCCESS_CREDMAN_SELECTOR,
+                                        resultData);
+                        } else {
+                            Slog.w(TAG, "View node not found after GetCredentialResponse");
+                        }
+                    } else {
+                        mClient.onGetCredentialResponse(id, viewId, response);
+                    }
                 } else {
                     Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
                             + "and exception");
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index f82a6aa..748253f 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -106,7 +106,7 @@
     private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
     private static final int DEFAULT_MAX_FILES = 1000;
     private static final int DEFAULT_MAX_FILES_LOWRAM = 300;
-    private static final int DEFAULT_QUOTA_KB = 10 * 1024;
+    private static final int DEFAULT_QUOTA_KB = Build.IS_USERDEBUG ? 20 * 1024 : 10 * 1024;
     private static final int DEFAULT_QUOTA_PERCENT = 10;
     private static final int DEFAULT_RESERVE_PERCENT = 0;
     private static final int QUOTA_RESCAN_MILLIS = 5000;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 8dc15ad..25337a4 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -100,7 +100,7 @@
             "file_patterns": ["VcnManagementService\\.java"]
         },
         {
-            "name": "FrameworksNetTests",
+            "name": "FrameworksVpnTests",
             "options": [
                 {
                     "exclude-annotation": "com.android.testutils.SkipPresubmit"
@@ -163,9 +163,6 @@
                 }
             ],
             "file_patterns": ["PinnerService\\.java"]
-        },
-        {
-            "name": "FrameworksVpnTests"
         }
     ]
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cfe1e18..5e36709 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -141,7 +141,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -726,29 +725,19 @@
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
     final boolean mUseFifoUiScheduling;
 
-    // Use an offload queue for long broadcasts, e.g. BOOT_COMPLETED.
-    // For simplicity, since we statically declare the size of the array of BroadcastQueues,
-    // we still create this new offload queue, but never ever put anything on it.
-    final boolean mEnableOffloadQueue;
-
     /**
      * Flag indicating if we should use {@link BroadcastQueueModernImpl} instead
      * of the default {@link BroadcastQueueImpl}.
      */
     final boolean mEnableModernQueue;
 
-    static final int BROADCAST_QUEUE_FG = 0;
-    static final int BROADCAST_QUEUE_BG = 1;
-    static final int BROADCAST_QUEUE_BG_OFFLOAD = 2;
-    static final int BROADCAST_QUEUE_FG_OFFLOAD = 3;
-
     @GuardedBy("this")
     private final SparseArray<IUnsafeIntentStrictModeCallback>
             mStrictModeCallbacks = new SparseArray<>();
 
     // Convenient for easy iteration over the queues. Foreground is first
     // so that dispatch of foreground broadcasts gets precedence.
-    final BroadcastQueue[] mBroadcastQueues;
+    private BroadcastQueue mBroadcastQueue;
 
     @GuardedBy("this")
     BroadcastStats mLastBroadcastStats;
@@ -758,43 +747,6 @@
 
     TraceErrorLogger mTraceErrorLogger;
 
-    BroadcastQueue broadcastQueueForIntent(Intent intent) {
-        return broadcastQueueForFlags(intent.getFlags(), intent);
-    }
-
-    BroadcastQueue broadcastQueueForFlags(int flags) {
-        return broadcastQueueForFlags(flags, null);
-    }
-
-    BroadcastQueue broadcastQueueForFlags(int flags, Object cookie) {
-        if (mEnableModernQueue) {
-            return mBroadcastQueues[0];
-        }
-
-        if (isOnFgOffloadQueue(flags)) {
-            if (DEBUG_BROADCAST_BACKGROUND) {
-                Slog.i(TAG_BROADCAST,
-                        "Broadcast intent " + cookie + " on foreground offload queue");
-            }
-            return mBroadcastQueues[BROADCAST_QUEUE_FG_OFFLOAD];
-        }
-
-        if (isOnBgOffloadQueue(flags)) {
-            if (DEBUG_BROADCAST_BACKGROUND) {
-                Slog.i(TAG_BROADCAST,
-                        "Broadcast intent " + cookie + " on background offload queue");
-            }
-            return mBroadcastQueues[BROADCAST_QUEUE_BG_OFFLOAD];
-        }
-
-        final boolean isFg = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
-        if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
-                "Broadcast intent " + cookie + " on "
-                + (isFg ? "foreground" : "background") + " queue");
-        return (isFg) ? mBroadcastQueues[BROADCAST_QUEUE_FG]
-                : mBroadcastQueues[BROADCAST_QUEUE_BG];
-    }
-
     private volatile int mDeviceOwnerUid = INVALID_UID;
 
     /**
@@ -2556,9 +2508,8 @@
         mInternal = new LocalService();
         mPendingStartActivityUids = new PendingStartActivityUids();
         mUseFifoUiScheduling = false;
-        mEnableOffloadQueue = false;
         mEnableModernQueue = false;
-        mBroadcastQueues = injector.getBroadcastQueues(this);
+        mBroadcastQueue = injector.getBroadcastQueue(this);
         mComponentAliasResolver = new ComponentAliasResolver(this);
     }
 
@@ -2599,12 +2550,10 @@
                 ? new OomAdjusterModernImpl(this, mProcessList, activeUids)
                 : new OomAdjuster(this, mProcessList, activeUids);
 
-        mEnableOffloadQueue = SystemProperties.getBoolean(
-                "persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
         mEnableModernQueue = new BroadcastConstants(
                 Settings.Global.BROADCAST_FG_CONSTANTS).MODERN_QUEUE_ENABLED;
 
-        mBroadcastQueues = mInjector.getBroadcastQueues(this);
+        mBroadcastQueue = mInjector.getBroadcastQueue(this);
 
         mServices = new ActiveServices(this);
         mCpHelper = new ContentProviderHelper(this, true);
@@ -2671,6 +2620,14 @@
         mComponentAliasResolver = new ComponentAliasResolver(this);
     }
 
+    void setBroadcastQueueForTest(BroadcastQueue broadcastQueue) {
+        mBroadcastQueue = broadcastQueue;
+    }
+
+    BroadcastQueue getBroadcastQueue() {
+        return mBroadcastQueue;
+    }
+
     public void setSystemServiceManager(SystemServiceManager mgr) {
         mSystemServiceManager = mgr;
     }
@@ -4280,19 +4237,14 @@
         }
 
         // Clean-up disabled broadcast receivers.
-        for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
-            mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
-                    packageName, disabledClasses, userId);
-        }
+        mBroadcastQueue.cleanupDisabledPackageReceiversLocked(
+                packageName, disabledClasses, userId);
 
     }
 
     final boolean clearBroadcastQueueForUserLocked(int userId) {
-        boolean didSomething = false;
-        for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
-            didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
-                    null, null, userId);
-        }
+        boolean didSomething = mBroadcastQueue.cleanupDisabledPackageReceiversLocked(
+                null, null, userId);
         return didSomething;
     }
 
@@ -4445,10 +4397,8 @@
         mUgmInternal.removeUriPermissionsForPackage(packageName, userId, false, false);
 
         if (doit) {
-            for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
-                didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
+            didSomething |= mBroadcastQueue.cleanupDisabledPackageReceiversLocked(
                         packageName, null, userId);
-            }
         }
 
         if (packageName == null || uninstalling || packageStateStopped) {
@@ -4515,9 +4465,7 @@
                 // Take care of any services that are waiting for the process.
                 mServices.processStartTimedOutLocked(app);
                 // Take care of any broadcasts waiting for the process.
-                for (BroadcastQueue queue : mBroadcastQueues) {
-                    queue.onApplicationTimeoutLocked(app);
-                }
+                mBroadcastQueue.onApplicationTimeoutLocked(app);
                 if (!isKillTimeout) {
                     mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                     app.killLocked("start timeout",
@@ -4959,9 +4907,7 @@
             // Check if a next-broadcast receiver is in this process...
             if (!badApp) {
                 try {
-                    for (BroadcastQueue queue : mBroadcastQueues) {
-                        didSomething |= queue.onApplicationAttachedLocked(app);
-                    }
+                    didSomething |= mBroadcastQueue.onApplicationAttachedLocked(app);
                     checkTime(startTime, "finishAttachApplicationInner: "
                             + "after dispatching broadcasts");
                 } catch (BroadcastDeliveryFailedException e) {
@@ -9101,9 +9047,7 @@
     }
 
     private void startBroadcastObservers() {
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.start(mContext.getContentResolver());
-        }
+        mBroadcastQueue.start(mContext.getContentResolver());
     }
 
     private void updateForceBackgroundCheck(boolean enabled) {
@@ -11509,9 +11453,7 @@
             }
         }
         mReceiverResolver.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
-        for (BroadcastQueue q : mBroadcastQueues) {
-            q.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
-        }
+        mBroadcastQueue.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
         synchronized (mStickyBroadcasts) {
             for (int user = 0; user < mStickyBroadcasts.size(); user++) {
                 long token = proto.start(
@@ -11661,11 +11603,9 @@
         }
 
         if (!onlyReceivers) {
-            for (BroadcastQueue q : mBroadcastQueues) {
-                needSep = q.dumpLocked(fd, pw, args, opti,
-                        dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep);
-                printedAnything |= needSep;
-            }
+            needSep = mBroadcastQueue.dumpLocked(fd, pw, args, opti,
+                    dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep);
+            printedAnything |= needSep;
         }
 
         needSep = true;
@@ -11721,9 +11661,8 @@
 
         if (!onlyHistory && !onlyReceivers && dumpAll) {
             pw.println();
-            for (BroadcastQueue queue : mBroadcastQueues) {
-                pw.println("  Queue " + queue.toString() + ": " + queue.describeStateLocked());
-            }
+            pw.println("  Queue " + mBroadcastQueue.toString() + ": "
+                    + mBroadcastQueue.describeStateLocked());
             pw.println("  mHandler:");
             mHandler.dump(new PrintWriterPrinter(pw), "    ");
             needSep = true;
@@ -13571,9 +13510,7 @@
             mOomAdjuster.mCachedAppOptimizer.onCleanupApplicationRecordLocked(app);
         }
         mAppProfiler.onCleanupApplicationRecordLocked(app);
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.onApplicationCleanupLocked(app);
-        }
+        mBroadcastQueue.onApplicationCleanupLocked(app);
         clearProcessForegroundLocked(app);
         mServices.killServicesLocked(app, allowRestart);
         mPhantomProcessList.onAppDied(pid);
@@ -14687,7 +14624,7 @@
                             originalStickyCallingUid))) {
                         sticky = broadcast.intent;
                     }
-                    BroadcastQueue queue = broadcastQueueForIntent(broadcast.intent);
+                    BroadcastQueue queue = mBroadcastQueue;
                     BroadcastRecord r = new BroadcastRecord(queue, broadcast.intent, null,
                             null, null, -1, -1, false, null, null, null, null, OP_NONE,
                             BroadcastOptions.makeWithDeferUntilActive(broadcast.deferUntilActive),
@@ -15686,7 +15623,7 @@
                 checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                         isProtectedBroadcast, registeredReceivers);
             }
-            final BroadcastQueue queue = broadcastQueueForIntent(intent);
+            final BroadcastQueue queue = mBroadcastQueue;
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
@@ -15779,7 +15716,7 @@
 
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
-            BroadcastQueue queue = broadcastQueueForIntent(intent);
+            BroadcastQueue queue = mBroadcastQueue;
             filterNonExportedComponents(intent, callingUid, callingPid, receivers,
                     mPlatformCompat, callerPackage, resolvedType);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
@@ -16067,9 +16004,7 @@
     }
 
     void backgroundServicesFinishedLocked(int userId) {
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.backgroundServicesFinishedLocked(userId);
-        }
+        mBroadcastQueue.backgroundServicesFinishedLocked(userId);
     }
 
     public void finishReceiver(IBinder caller, int resultCode, String resultData,
@@ -16090,8 +16025,7 @@
                     return;
                 }
 
-                final BroadcastQueue queue = broadcastQueueForFlags(flags);
-                queue.finishReceiverLocked(callerApp, resultCode,
+                mBroadcastQueue.finishReceiverLocked(callerApp, resultCode,
                         resultData, resultExtras, resultAbort, true);
                 // updateOomAdjLocked() will be done here
                 trimApplicationsLocked(false, OOM_ADJ_REASON_FINISH_RECEIVER);
@@ -16682,10 +16616,7 @@
     // =========================================================
 
     boolean isReceivingBroadcastLocked(ProcessRecord app, int[] outSchedGroup) {
-        int res = ProcessList.SCHED_GROUP_UNDEFINED;
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            res = Math.max(res, queue.getPreferredSchedulingGroupLocked(app));
-        }
+        final int res = mBroadcastQueue.getPreferredSchedulingGroupLocked(app);
         outSchedGroup[0] = res;
         return res != ProcessList.SCHED_GROUP_UNDEFINED;
     }
@@ -16809,10 +16740,8 @@
      */
     @GuardedBy("this")
     final boolean canGcNowLocked() {
-        for (BroadcastQueue q : mBroadcastQueues) {
-            if (!q.isIdleLocked()) {
-                return false;
-            }
+        if (!mBroadcastQueue.isIdleLocked()) {
+            return false;
         }
         return mAtmInternal.canGcNow();
     }
@@ -18022,9 +17951,7 @@
     }
 
     void onProcessFreezableChangedLocked(ProcessRecord app) {
-        if (mEnableModernQueue) {
-            mBroadcastQueues[0].onProcessFreezableChangedLocked(app);
-        }
+        mBroadcastQueue.onProcessFreezableChangedLocked(app);
     }
 
     @VisibleForTesting
@@ -19715,9 +19642,7 @@
         if (flushBroadcastLoopers) {
             BroadcastLoopers.waitForIdle(pw);
         }
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.waitForIdle(pw);
-        }
+        mBroadcastQueue.waitForIdle(pw);
         pw.println("All broadcast queues are idle!");
         pw.flush();
     }
@@ -19733,9 +19658,7 @@
         if (flushBroadcastLoopers) {
             BroadcastLoopers.waitForBarrier(pw);
         }
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.waitForBarrier(pw);
-        }
+        mBroadcastQueue.waitForBarrier(pw);
         if (flushApplicationThreads) {
             waitForApplicationBarrier(pw);
         }
@@ -19811,9 +19734,7 @@
 
     void waitForBroadcastDispatch(@NonNull PrintWriter pw, @NonNull Intent intent) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastDispatch");
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.waitForDispatched(intent, pw);
-        }
+        mBroadcastQueue.waitForDispatched(intent, pw);
     }
 
     void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
@@ -19858,9 +19779,7 @@
             return;
         }
 
-        for (BroadcastQueue queue : mBroadcastQueues) {
-            queue.forceDelayBroadcastDelivery(targetPackage, delayedDurationMs);
-        }
+        mBroadcastQueue.forceDelayBroadcastDelivery(targetPackage, delayedDurationMs);
     }
 
     @Override
@@ -20414,7 +20333,7 @@
             return mNmi != null;
         }
 
-        public BroadcastQueue[] getBroadcastQueues(ActivityManagerService service) {
+        public BroadcastQueue getBroadcastQueue(ActivityManagerService service) {
             // Broadcast policy parameters
             final BroadcastConstants foreConstants = new BroadcastConstants(
                     Settings.Global.BROADCAST_FG_CONSTANTS);
@@ -20430,26 +20349,8 @@
             // by default, no "slow" policy in this queue
             offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
 
-            final BroadcastQueue[] broadcastQueues;
-            final Handler handler = service.mHandler;
-            if (service.mEnableModernQueue) {
-                broadcastQueues = new BroadcastQueue[1];
-                broadcastQueues[0] = new BroadcastQueueModernImpl(service, handler,
+            return new BroadcastQueueModernImpl(service, service.mHandler,
                         foreConstants, backConstants);
-            } else {
-                broadcastQueues = new BroadcastQueue[4];
-                broadcastQueues[BROADCAST_QUEUE_FG] = new BroadcastQueueImpl(service, handler,
-                        "foreground", foreConstants, false, ProcessList.SCHED_GROUP_DEFAULT);
-                broadcastQueues[BROADCAST_QUEUE_BG] = new BroadcastQueueImpl(service, handler,
-                        "background", backConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
-                broadcastQueues[BROADCAST_QUEUE_BG_OFFLOAD] = new BroadcastQueueImpl(service,
-                        handler, "offload_bg", offloadConstants, true,
-                        ProcessList.SCHED_GROUP_BACKGROUND);
-                broadcastQueues[BROADCAST_QUEUE_FG_OFFLOAD] = new BroadcastQueueImpl(service,
-                        handler, "offload_fg", foreConstants, true,
-                        ProcessList.SCHED_GROUP_BACKGROUND);
-            }
-            return broadcastQueues;
         }
 
         /** @see Binder#getCallingUid */
@@ -20771,14 +20672,6 @@
         }
     }
 
-    private boolean isOnFgOffloadQueue(int flags) {
-        return ((flags & Intent.FLAG_RECEIVER_OFFLOAD_FOREGROUND) != 0);
-    }
-
-    private boolean isOnBgOffloadQueue(int flags) {
-        return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
-    }
-
     @Override
     public ParcelFileDescriptor getLifeMonitor() {
         if (!isCallerShell()) {
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
deleted file mode 100644
index 8aa3921..0000000
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
-import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UptimeMillisLong;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AlarmManagerInternal;
-import com.android.server.LocalServices;
-
-import dalvik.annotation.optimization.NeverCompile;
-
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Set;
-
-/**
- * Manages ordered broadcast delivery, applying policy to mitigate the effects of
- * slow receivers.
- */
-public class BroadcastDispatcher {
-    private static final String TAG = "BroadcastDispatcher";
-
-    // Deferred broadcasts to one app; times are all uptime time base like
-    // other broadcast-related timekeeping
-    static class Deferrals {
-        final int uid;
-        long deferredAt;    // when we started deferring
-        long deferredBy;    // how long did we defer by last time?
-        long deferUntil;    // when does the next element become deliverable?
-        int alarmCount;
-
-        final ArrayList<BroadcastRecord> broadcasts;
-
-        Deferrals(int uid, long now, long backoff, int count) {
-            this.uid = uid;
-            this.deferredAt = now;
-            this.deferredBy = backoff;
-            this.deferUntil = now + backoff;
-            this.alarmCount = count;
-            broadcasts = new ArrayList<>();
-        }
-
-        void add(BroadcastRecord br) {
-            broadcasts.add(br);
-        }
-
-        int size() {
-            return broadcasts.size();
-        }
-
-        boolean isEmpty() {
-            return broadcasts.isEmpty();
-        }
-
-        @NeverCompile
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
-            for (BroadcastRecord br : broadcasts) {
-                br.dumpDebug(proto, fieldId);
-            }
-        }
-
-        @NeverCompile
-        void dumpLocked(Dumper d) {
-            for (BroadcastRecord br : broadcasts) {
-                d.dump(br);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append("Deferrals{uid=");
-            sb.append(uid);
-            sb.append(", deferUntil=");
-            sb.append(deferUntil);
-            sb.append(", #broadcasts=");
-            sb.append(broadcasts.size());
-            sb.append("}");
-            return sb.toString();
-        }
-    }
-
-    // Carrying dump formatting state across multiple concatenated datasets
-    class Dumper {
-        final PrintWriter mPw;
-        final String mQueueName;
-        final String mDumpPackage;
-        final SimpleDateFormat mSdf;
-        boolean mPrinted;
-        boolean mNeedSep;
-        String mHeading;
-        String mLabel;
-        int mOrdinal;
-
-        Dumper(PrintWriter pw, String queueName, String dumpPackage, SimpleDateFormat sdf) {
-            mPw = pw;
-            mQueueName = queueName;
-            mDumpPackage = dumpPackage;
-            mSdf = sdf;
-
-            mPrinted = false;
-            mNeedSep = true;
-        }
-
-        void setHeading(String heading) {
-            mHeading = heading;
-            mPrinted = false;
-        }
-
-        void setLabel(String label) {
-            //"  Active Ordered Broadcast " + mQueueName + " #" + i + ":"
-            mLabel = "  " + label + " " + mQueueName + " #";
-            mOrdinal = 0;
-        }
-
-        boolean didPrint() {
-            return mPrinted;
-        }
-
-        @NeverCompile
-        void dump(BroadcastRecord br) {
-            if (mDumpPackage == null || mDumpPackage.equals(br.callerPackage)) {
-                if (!mPrinted) {
-                    if (mNeedSep) {
-                        mPw.println();
-                    }
-                    mPrinted = true;
-                    mNeedSep = true;
-                    mPw.println("  " + mHeading + " [" + mQueueName + "]:");
-                }
-                mPw.println(mLabel + mOrdinal + ":");
-                mOrdinal++;
-
-                br.dump(mPw, "    ", mSdf);
-            }
-        }
-    }
-
-    private final Object mLock;
-    private final BroadcastQueueImpl mQueue;
-    private final BroadcastConstants mConstants;
-    private final Handler mHandler;
-    private AlarmManagerInternal mAlarm;
-
-    // Current alarm targets; mapping uid -> in-flight alarm count
-    final SparseIntArray mAlarmUids = new SparseIntArray();
-    final AlarmManagerInternal.InFlightListener mAlarmListener =
-            new AlarmManagerInternal.InFlightListener() {
-        @Override
-        public void broadcastAlarmPending(final int recipientUid) {
-            synchronized (mLock) {
-                final int newCount = mAlarmUids.get(recipientUid, 0) + 1;
-                mAlarmUids.put(recipientUid, newCount);
-                // any deferred broadcasts to this app now get fast-tracked
-                final int numEntries = mDeferredBroadcasts.size();
-                for (int i = 0; i < numEntries; i++) {
-                    if (recipientUid == mDeferredBroadcasts.get(i).uid) {
-                        Deferrals d = mDeferredBroadcasts.remove(i);
-                        mAlarmDeferrals.add(d);
-                        break;
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void broadcastAlarmComplete(final int recipientUid) {
-            synchronized (mLock) {
-                final int newCount = mAlarmUids.get(recipientUid, 0) - 1;
-                if (newCount >= 0) {
-                    mAlarmUids.put(recipientUid, newCount);
-                } else {
-                    Slog.wtf(TAG, "Undercount of broadcast alarms in flight for " + recipientUid);
-                    mAlarmUids.put(recipientUid, 0);
-                }
-
-                // No longer an alarm target, so resume ordinary deferral policy
-                if (newCount <= 0) {
-                    final int numEntries = mAlarmDeferrals.size();
-                    for (int i = 0; i < numEntries; i++) {
-                        if (recipientUid == mAlarmDeferrals.get(i).uid) {
-                            Deferrals d = mAlarmDeferrals.remove(i);
-                            insertLocked(mDeferredBroadcasts, d);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    };
-
-    // Queue recheck operation used to tickle broadcast delivery when appropriate
-    final Runnable mScheduleRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mLock) {
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.v(TAG, "Deferral recheck of pending broadcasts");
-                }
-                mQueue.scheduleBroadcastsLocked();
-                mRecheckScheduled = false;
-            }
-        }
-    };
-    private boolean mRecheckScheduled = false;
-
-    // Usual issuance-order outbound queue
-    private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
-    // General deferrals not holding up alarms
-    private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
-    // Deferrals that *are* holding up alarms; ordered by alarm dispatch time
-    private final ArrayList<Deferrals> mAlarmDeferrals = new ArrayList<>();
-    // Under the "deliver alarm broadcasts immediately" policy, the queue of
-    // upcoming alarm broadcasts.  These are always delivered first - if the
-    // policy is changed on the fly from immediate-alarm-delivery to the previous
-    // in-order-queueing behavior, pending immediate alarm deliveries will drain
-    // and then the behavior settle into the pre-U semantics.
-    private final ArrayList<BroadcastRecord> mAlarmQueue = new ArrayList<>();
-
-    // Next outbound broadcast, established by getNextBroadcastLocked()
-    private BroadcastRecord mCurrentBroadcast;
-
-    // Map userId to its deferred boot completed broadcasts.
-    private SparseArray<DeferredBootCompletedBroadcastPerUser> mUser2Deferred = new SparseArray<>();
-
-    /**
-     * Deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts that is sent to a user.
-     */
-    static class DeferredBootCompletedBroadcastPerUser {
-        private int mUserId;
-        // UID that has process started at least once, ready to execute LOCKED_BOOT_COMPLETED
-        // receivers.
-        @VisibleForTesting
-        SparseBooleanArray mUidReadyForLockedBootCompletedBroadcast = new SparseBooleanArray();
-        // UID that has process started at least once, ready to execute BOOT_COMPLETED receivers.
-        @VisibleForTesting
-        SparseBooleanArray mUidReadyForBootCompletedBroadcast = new SparseBooleanArray();
-        // Map UID to deferred LOCKED_BOOT_COMPLETED broadcasts.
-        // LOCKED_BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has
-        // any process started.
-        @VisibleForTesting
-        SparseArray<BroadcastRecord> mDeferredLockedBootCompletedBroadcasts = new SparseArray<>();
-        // is the LOCKED_BOOT_COMPLETED broadcast received by the user.
-        @VisibleForTesting
-        boolean mLockedBootCompletedBroadcastReceived;
-        // Map UID to deferred BOOT_COMPLETED broadcasts.
-        // BOOT_COMPLETED broadcast receivers are deferred until the first time the uid has any
-        // process started.
-        @VisibleForTesting
-        SparseArray<BroadcastRecord> mDeferredBootCompletedBroadcasts = new SparseArray<>();
-        // is the BOOT_COMPLETED broadcast received by the user.
-        @VisibleForTesting
-        boolean mBootCompletedBroadcastReceived;
-
-        DeferredBootCompletedBroadcastPerUser(int userId) {
-            this.mUserId = userId;
-        }
-
-        public void updateUidReady(int uid) {
-            if (!mLockedBootCompletedBroadcastReceived
-                    || mDeferredLockedBootCompletedBroadcasts.size() != 0) {
-                mUidReadyForLockedBootCompletedBroadcast.put(uid, true);
-            }
-            if (!mBootCompletedBroadcastReceived
-                    || mDeferredBootCompletedBroadcasts.size() != 0) {
-                mUidReadyForBootCompletedBroadcast.put(uid, true);
-            }
-        }
-
-        public void enqueueBootCompletedBroadcasts(String action,
-                SparseArray<BroadcastRecord> deferred) {
-            if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
-                enqueueBootCompletedBroadcasts(deferred, mDeferredLockedBootCompletedBroadcasts,
-                        mUidReadyForLockedBootCompletedBroadcast);
-                mLockedBootCompletedBroadcastReceived = true;
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    dumpBootCompletedBroadcastRecord(mDeferredLockedBootCompletedBroadcasts);
-                }
-            } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                enqueueBootCompletedBroadcasts(deferred, mDeferredBootCompletedBroadcasts,
-                        mUidReadyForBootCompletedBroadcast);
-                mBootCompletedBroadcastReceived = true;
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    dumpBootCompletedBroadcastRecord(mDeferredBootCompletedBroadcasts);
-                }
-            }
-        }
-
-        /**
-         * Merge UID to BroadcastRecord map into {@link #mDeferredBootCompletedBroadcasts} or
-         * {@link #mDeferredLockedBootCompletedBroadcasts}
-         * @param from the UID to BroadcastRecord map.
-         * @param into The UID to list of BroadcastRecord map.
-         */
-        private void enqueueBootCompletedBroadcasts(SparseArray<BroadcastRecord> from,
-                SparseArray<BroadcastRecord> into, SparseBooleanArray uidReadyForReceiver) {
-            // remove unwanted uids from uidReadyForReceiver.
-            for (int i = uidReadyForReceiver.size() - 1; i >= 0; i--) {
-                if (from.indexOfKey(uidReadyForReceiver.keyAt(i)) < 0) {
-                    uidReadyForReceiver.removeAt(i);
-                }
-            }
-            for (int i = 0, size = from.size(); i < size; i++) {
-                final int uid = from.keyAt(i);
-                into.put(uid, from.valueAt(i));
-                if (uidReadyForReceiver.indexOfKey(uid) < 0) {
-                    // uid is wanted but not ready.
-                    uidReadyForReceiver.put(uid, false);
-                }
-            }
-        }
-
-        public @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
-                boolean isAllUidReady) {
-            BroadcastRecord next = dequeueDeferredBootCompletedBroadcast(
-                    mDeferredLockedBootCompletedBroadcasts,
-                    mUidReadyForLockedBootCompletedBroadcast, isAllUidReady);
-            if (next == null) {
-                next = dequeueDeferredBootCompletedBroadcast(mDeferredBootCompletedBroadcasts,
-                        mUidReadyForBootCompletedBroadcast, isAllUidReady);
-            }
-            return next;
-        }
-
-        private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast(
-                SparseArray<BroadcastRecord> uid2br, SparseBooleanArray uidReadyForReceiver,
-                boolean isAllUidReady) {
-            for (int i = 0, size = uid2br.size(); i < size; i++) {
-                final int uid = uid2br.keyAt(i);
-                if (isAllUidReady || uidReadyForReceiver.get(uid)) {
-                    final BroadcastRecord br = uid2br.valueAt(i);
-                    if (DEBUG_BROADCAST_DEFERRAL) {
-                        final Object receiver = br.receivers.get(0);
-                        if (receiver instanceof BroadcastFilter) {
-                            if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
-                                        + " BroadcastFilter:" + (BroadcastFilter) receiver
-                                        + " broadcast:" + br.intent.getAction());
-                            }
-                        } else /* if (receiver instanceof ResolveInfo) */ {
-                            ResolveInfo info = (ResolveInfo) receiver;
-                            String packageName = info.activityInfo.applicationInfo.packageName;
-                            if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG, "getDeferredBootCompletedBroadcast uid:" + uid
-                                        + " packageName:" + packageName
-                                        + " broadcast:" + br.intent.getAction());
-                            }
-                        }
-                    }
-                    // remove the BroadcastRecord.
-                    uid2br.removeAt(i);
-                    if (uid2br.size() == 0) {
-                        // All deferred receivers are executed, do not need uidReadyForReceiver
-                        // any more.
-                        uidReadyForReceiver.clear();
-                    }
-                    return br;
-                }
-            }
-            return null;
-        }
-
-        private @Nullable SparseArray<BroadcastRecord> getDeferredList(String action) {
-            SparseArray<BroadcastRecord> brs = null;
-            if (action.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)) {
-                brs = mDeferredLockedBootCompletedBroadcasts;
-            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                brs = mDeferredBootCompletedBroadcasts;
-            }
-            return brs;
-        }
-
-        /**
-         * Return the total number of UIDs in all BroadcastRecord in
-         * {@link #mDeferredBootCompletedBroadcasts} or
-         * {@link #mDeferredLockedBootCompletedBroadcasts}
-         */
-        private int getBootCompletedBroadcastsUidsSize(String action) {
-            SparseArray<BroadcastRecord> brs = getDeferredList(action);
-            return brs != null ? brs.size() : 0;
-        }
-
-        /**
-         * Return the total number of receivers in all BroadcastRecord in
-         * {@link #mDeferredBootCompletedBroadcasts} or
-         * {@link #mDeferredLockedBootCompletedBroadcasts}
-         */
-        private int getBootCompletedBroadcastsReceiversSize(String action) {
-            SparseArray<BroadcastRecord> brs = getDeferredList(action);
-            if (brs == null) {
-                return 0;
-            }
-            int size = 0;
-            for (int i = 0, s = brs.size(); i < s; i++) {
-                size += brs.valueAt(i).receivers.size();
-            }
-            return size;
-        }
-
-        @NeverCompile
-        public void dump(Dumper dumper, String action) {
-            SparseArray<BroadcastRecord> brs = getDeferredList(action);
-            if (brs == null) {
-                return;
-            }
-            for (int i = 0, size = brs.size(); i < size; i++) {
-                dumper.dump(brs.valueAt(i));
-            }
-        }
-
-        @NeverCompile
-        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-            for (int i = 0, size = mDeferredLockedBootCompletedBroadcasts.size(); i < size; i++) {
-                mDeferredLockedBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
-            }
-            for (int i = 0, size = mDeferredBootCompletedBroadcasts.size(); i < size; i++) {
-                mDeferredBootCompletedBroadcasts.valueAt(i).dumpDebug(proto, fieldId);
-            }
-        }
-
-        @NeverCompile
-        private void dumpBootCompletedBroadcastRecord(SparseArray<BroadcastRecord> brs) {
-            for (int i = 0, size = brs.size(); i < size; i++) {
-                final Object receiver = brs.valueAt(i).receivers.get(0);
-                String packageName = null;
-                if (receiver instanceof BroadcastFilter) {
-                    BroadcastFilter recv = (BroadcastFilter) receiver;
-                    packageName = recv.receiverList.app.processName;
-                } else /* if (receiver instanceof ResolveInfo) */ {
-                    ResolveInfo info = (ResolveInfo) receiver;
-                    packageName = info.activityInfo.applicationInfo.packageName;
-                }
-                Slog.i(TAG, "uid:" + brs.keyAt(i)
-                        + " packageName:" + packageName
-                        + " receivers:" + brs.valueAt(i).receivers.size());
-            }
-        }
-    }
-
-    private DeferredBootCompletedBroadcastPerUser getDeferredPerUser(int userId) {
-        if (mUser2Deferred.contains(userId)) {
-            return mUser2Deferred.get(userId);
-        } else {
-            final DeferredBootCompletedBroadcastPerUser temp =
-                    new DeferredBootCompletedBroadcastPerUser(userId);
-            mUser2Deferred.put(userId, temp);
-            return temp;
-        }
-    }
-
-    /**
-     * ActivityManagerService.attachApplication() call this method to notify that the UID is ready
-     * to accept deferred LOCKED_BOOT_COMPLETED and BOOT_COMPLETED broadcasts.
-     * @param uid
-     */
-    public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
-        getDeferredPerUser(UserHandle.getUserId(uid)).updateUidReady(uid);
-    }
-
-    private @Nullable BroadcastRecord dequeueDeferredBootCompletedBroadcast() {
-        final boolean isAllUidReady = (mQueue.mService.mConstants.mDeferBootCompletedBroadcast
-                == DEFER_BOOT_COMPLETED_BROADCAST_NONE);
-        BroadcastRecord next = null;
-        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
-            next = mUser2Deferred.valueAt(i).dequeueDeferredBootCompletedBroadcast(isAllUidReady);
-            if (next != null) {
-                break;
-            }
-        }
-        return next;
-    }
-
-    /**
-     * Constructed & sharing a lock with its associated BroadcastQueue instance
-     */
-    public BroadcastDispatcher(BroadcastQueueImpl queue, BroadcastConstants constants,
-            Handler handler, Object lock) {
-        mQueue = queue;
-        mConstants = constants;
-        mHandler = handler;
-        mLock = lock;
-    }
-
-    /**
-     * Spin up the integration with the alarm manager service; done lazily to manage
-     * service availability ordering during boot.
-     */
-    public void start() {
-        // Set up broadcast alarm tracking
-        mAlarm = LocalServices.getService(AlarmManagerInternal.class);
-        mAlarm.registerInFlightListener(mAlarmListener);
-    }
-
-    /**
-     * Standard contents-are-empty check
-     */
-    public boolean isEmpty() {
-        synchronized (mLock) {
-            return isIdle()
-                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED) == 0
-                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED) == 0;
-        }
-    }
-
-    /**
-     * Have less check than {@link #isEmpty()}.
-     * The dispatcher is considered as idle even with deferred LOCKED_BOOT_COMPLETED/BOOT_COMPLETED
-     * broadcasts because those can be deferred until the first time the uid's process is started.
-     * @return
-     */
-    public boolean isIdle() {
-        synchronized (mLock) {
-            return mCurrentBroadcast == null
-                    && mOrderedBroadcasts.isEmpty()
-                    && mAlarmQueue.isEmpty()
-                    && isDeferralsListEmpty(mDeferredBroadcasts)
-                    && isDeferralsListEmpty(mAlarmDeferrals);
-        }
-    }
-
-    private static boolean isDeferralsBeyondBarrier(@NonNull ArrayList<Deferrals> list,
-            @UptimeMillisLong long barrierTime) {
-        for (int i = 0; i < list.size(); i++) {
-            if (!isBeyondBarrier(list.get(i).broadcasts, barrierTime)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static boolean isBeyondBarrier(@NonNull ArrayList<BroadcastRecord> list,
-            @UptimeMillisLong long barrierTime) {
-        for (int i = 0; i < list.size(); i++) {
-            if (list.get(i).enqueueTime <= barrierTime) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public boolean isBeyondBarrier(@UptimeMillisLong long barrierTime) {
-        synchronized (mLock) {
-            if ((mCurrentBroadcast != null) && mCurrentBroadcast.enqueueTime <= barrierTime) {
-                return false;
-            }
-            return isBeyondBarrier(mOrderedBroadcasts, barrierTime)
-                    && isBeyondBarrier(mAlarmQueue, barrierTime)
-                    && isDeferralsBeyondBarrier(mDeferredBroadcasts, barrierTime)
-                    && isDeferralsBeyondBarrier(mAlarmDeferrals, barrierTime);
-        }
-    }
-
-    private static boolean isDispatchedInDeferrals(@NonNull ArrayList<Deferrals> list,
-            @NonNull Intent intent) {
-        for (int i = 0; i < list.size(); i++) {
-            if (!isDispatched(list.get(i).broadcasts, intent)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static boolean isDispatched(@NonNull ArrayList<BroadcastRecord> list,
-            @NonNull Intent intent) {
-        for (int i = 0; i < list.size(); i++) {
-            if (intent.filterEquals(list.get(i).intent)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public boolean isDispatched(@NonNull Intent intent) {
-        synchronized (mLock) {
-            if ((mCurrentBroadcast != null) && intent.filterEquals(mCurrentBroadcast.intent)) {
-                return false;
-            }
-            return isDispatched(mOrderedBroadcasts, intent)
-                    && isDispatched(mAlarmQueue, intent)
-                    && isDispatchedInDeferrals(mDeferredBroadcasts, intent)
-                    && isDispatchedInDeferrals(mAlarmDeferrals, intent);
-        }
-    }
-
-    private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
-        int pending = 0;
-        final int numEntries = list.size();
-        for (int i = 0; i < numEntries; i++) {
-            pending += list.get(i).size();
-        }
-        return pending;
-    }
-
-    private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
-        return pendingInDeferralsList(list) == 0;
-    }
-
-    /**
-     * Strictly for logging, describe the currently pending contents in a human-
-     * readable way
-     */
-    public String describeStateLocked() {
-        final StringBuilder sb = new StringBuilder(128);
-        if (mCurrentBroadcast != null) {
-            sb.append("1 in flight, ");
-        }
-        sb.append(mOrderedBroadcasts.size());
-        sb.append(" ordered");
-        int n = mAlarmQueue.size();
-        if (n > 0) {
-            sb.append(", ");
-            sb.append(n);
-            sb.append(" alarms");
-        }
-        n = pendingInDeferralsList(mAlarmDeferrals);
-        if (n > 0) {
-            sb.append(", ");
-            sb.append(n);
-            sb.append(" deferrals in alarm recipients");
-        }
-        n = pendingInDeferralsList(mDeferredBroadcasts);
-        if (n > 0) {
-            sb.append(", ");
-            sb.append(n);
-            sb.append(" deferred");
-        }
-        n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED);
-        if (n > 0) {
-            sb.append(", ");
-            sb.append(n);
-            sb.append(" deferred LOCKED_BOOT_COMPLETED/");
-            sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_LOCKED_BOOT_COMPLETED));
-            sb.append(" receivers");
-        }
-
-        n = getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED);
-        if (n > 0) {
-            sb.append(", ");
-            sb.append(n);
-            sb.append(" deferred BOOT_COMPLETED/");
-            sb.append(getBootCompletedBroadcastsReceiversSize(Intent.ACTION_BOOT_COMPLETED));
-            sb.append(" receivers");
-        }
-        return sb.toString();
-    }
-
-    // ----------------------------------
-    // BroadcastQueue operation support
-    void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
-        final ArrayList<BroadcastRecord> queue =
-                (r.alarm && mQueue.mService.mConstants.mPrioritizeAlarmBroadcasts)
-                        ? mAlarmQueue
-                        : mOrderedBroadcasts;
-
-        if (r.receivers == null || r.receivers.isEmpty()) {
-            // Fast no-op path for broadcasts that won't actually be dispatched to
-            // receivers - we still need to handle completion callbacks and historical
-            // records, but we don't need to consider the fancy cases.
-            queue.add(r);
-            return;
-        }
-
-        if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(r.intent.getAction())) {
-            // Create one BroadcastRecord for each UID that can be deferred.
-            final SparseArray<BroadcastRecord> deferred =
-                    r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
-                            mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
-            getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
-                    Intent.ACTION_LOCKED_BOOT_COMPLETED, deferred);
-            if (!r.receivers.isEmpty()) {
-                // The non-deferred receivers.
-                mOrderedBroadcasts.add(r);
-                return;
-            }
-        } else if (Intent.ACTION_BOOT_COMPLETED.equals(r.intent.getAction())) {
-            // Create one BroadcastRecord for each UID that can be deferred.
-            final SparseArray<BroadcastRecord> deferred =
-                    r.splitDeferredBootCompletedBroadcastLocked(mQueue.mService.mInternal,
-                            mQueue.mService.mConstants.mDeferBootCompletedBroadcast);
-            getDeferredPerUser(r.userId).enqueueBootCompletedBroadcasts(
-                    Intent.ACTION_BOOT_COMPLETED, deferred);
-            if (!r.receivers.isEmpty()) {
-                // The non-deferred receivers.
-                mOrderedBroadcasts.add(r);
-                return;
-            }
-        } else {
-            // Ordinary broadcast, so put it on the appropriate queue and carry on
-            queue.add(r);
-        }
-    }
-
-    /**
-     * Return the total number of UIDs in all deferred boot completed BroadcastRecord.
-     */
-    private int getBootCompletedBroadcastsUidsSize(String action) {
-        int size = 0;
-        for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
-            size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsUidsSize(action);
-        }
-        return size;
-    }
-
-    /**
-     * Return the total number of receivers in all deferred boot completed BroadcastRecord.
-     */
-    private int getBootCompletedBroadcastsReceiversSize(String action) {
-        int size = 0;
-        for (int i = 0, s = mUser2Deferred.size(); i < s; i++) {
-            size += mUser2Deferred.valueAt(i).getBootCompletedBroadcastsReceiversSize(action);
-        }
-        return size;
-    }
-
-    // Returns the now-replaced broadcast record, or null if none
-    BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
-        // Simple case, in the ordinary queue.
-        BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);
-        // ... or possibly in the simple alarm queue
-        if (old == null) {
-            old = replaceBroadcastLocked(mAlarmQueue, r, typeForLogging);
-        }
-        // If we didn't find it, less-simple:  in a deferral queue?
-        if (old == null) {
-            old = replaceDeferredBroadcastLocked(mAlarmDeferrals, r, typeForLogging);
-        }
-        if (old == null) {
-            old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
-        }
-        return old;
-    }
-
-    private BroadcastRecord replaceDeferredBroadcastLocked(ArrayList<Deferrals> list,
-            BroadcastRecord r, String typeForLogging) {
-        BroadcastRecord old;
-        final int numEntries = list.size();
-        for (int i = 0; i < numEntries; i++) {
-            final Deferrals d = list.get(i);
-            old = replaceBroadcastLocked(d.broadcasts, r, typeForLogging);
-            if (old != null) {
-                return old;
-            }
-        }
-        return null;
-    }
-
-    private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> list,
-            BroadcastRecord r, String typeForLogging) {
-        BroadcastRecord old;
-        final Intent intent = r.intent;
-        // Any in-flight broadcast has already been popped, and cannot be replaced.
-        // (This preserves existing behavior of the replacement API)
-        for (int i = list.size() - 1; i >= 0; i--) {
-            old = list.get(i);
-            if (old.userId == r.userId && intent.filterEquals(old.intent)) {
-                if (DEBUG_BROADCAST) {
-                    Slog.v(TAG, "***** Replacing " + typeForLogging
-                            + " [" + mQueue.mQueueName + "]: " + intent);
-                }
-                // Clone deferral state too if any
-                r.deferred = old.deferred;
-                list.set(i, r);
-                return old;
-            }
-        }
-        return null;
-    }
-
-    boolean cleanupDisabledPackageReceiversLocked(final String packageName,
-            Set<String> filterByClasses, final int userId, final boolean doit) {
-        // Note: fast short circuits when 'doit' is false, as soon as we hit any
-        // "yes we would do something" circumstance
-        boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
-                packageName, filterByClasses, userId, doit);
-        if (doit || !didSomething) {
-            didSomething = cleanupBroadcastListDisabledReceiversLocked(mAlarmQueue,
-                    packageName, filterByClasses, userId, doit);
-        }
-        if (doit || !didSomething) {
-            ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
-            for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
-                SparseArray<BroadcastRecord> brs =
-                        mUser2Deferred.valueAt(u).mDeferredLockedBootCompletedBroadcasts;
-                for (int i = 0, size = brs.size(); i < size; i++) {
-                    lockedBootCompletedBroadcasts.add(brs.valueAt(i));
-                }
-            }
-            didSomething = cleanupBroadcastListDisabledReceiversLocked(
-                    lockedBootCompletedBroadcasts,
-                    packageName, filterByClasses, userId, doit);
-        }
-        if (doit || !didSomething) {
-            ArrayList<BroadcastRecord> bootCompletedBroadcasts = new ArrayList<>();
-            for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
-                SparseArray<BroadcastRecord> brs =
-                        mUser2Deferred.valueAt(u).mDeferredBootCompletedBroadcasts;
-                for (int i = 0, size = brs.size(); i < size; i++) {
-                    bootCompletedBroadcasts.add(brs.valueAt(i));
-                }
-            }
-            didSomething = cleanupBroadcastListDisabledReceiversLocked(bootCompletedBroadcasts,
-                    packageName, filterByClasses, userId, doit);
-        }
-        if (doit || !didSomething) {
-            didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmDeferrals,
-                    packageName, filterByClasses, userId, doit);
-        }
-        if (doit || !didSomething) {
-            didSomething |= cleanupDeferralsListDisabledReceiversLocked(mDeferredBroadcasts,
-                    packageName, filterByClasses, userId, doit);
-        }
-        if ((doit || !didSomething) && mCurrentBroadcast != null) {
-            didSomething |= mCurrentBroadcast.cleanupDisabledPackageReceiversLocked(
-                    packageName, filterByClasses, userId, doit);
-        }
-
-        return didSomething;
-    }
-
-    private boolean cleanupDeferralsListDisabledReceiversLocked(ArrayList<Deferrals> list,
-            final String packageName, Set<String> filterByClasses, final int userId,
-            final boolean doit) {
-        boolean didSomething = false;
-        for (Deferrals d : list) {
-            didSomething = cleanupBroadcastListDisabledReceiversLocked(d.broadcasts,
-                    packageName, filterByClasses, userId, doit);
-            if (!doit && didSomething) {
-                return true;
-            }
-        }
-        return didSomething;
-    }
-
-    private boolean cleanupBroadcastListDisabledReceiversLocked(ArrayList<BroadcastRecord> list,
-            final String packageName, Set<String> filterByClasses, final int userId,
-            final boolean doit) {
-        boolean didSomething = false;
-        for (BroadcastRecord br : list) {
-            didSomething |= br.cleanupDisabledPackageReceiversLocked(packageName,
-                    filterByClasses, userId, doit);
-            if (!doit && didSomething) {
-                return true;
-            }
-        }
-        return didSomething;
-    }
-
-    /**
-     * Standard proto dump entry point
-     */
-    @NeverCompile
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        if (mCurrentBroadcast != null) {
-            mCurrentBroadcast.dumpDebug(proto, fieldId);
-        }
-        for (Deferrals d : mAlarmDeferrals) {
-            d.dumpDebug(proto, fieldId);
-        }
-        for (BroadcastRecord br : mOrderedBroadcasts) {
-            br.dumpDebug(proto, fieldId);
-        }
-        for (BroadcastRecord br : mAlarmQueue) {
-            br.dumpDebug(proto, fieldId);
-        }
-        for (Deferrals d : mDeferredBroadcasts) {
-            d.dumpDebug(proto, fieldId);
-        }
-
-        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
-            mUser2Deferred.valueAt(i).dumpDebug(proto, fieldId);
-        }
-    }
-
-    // ----------------------------------
-    // Dispatch & deferral management
-
-    public BroadcastRecord getActiveBroadcastLocked() {
-        return mCurrentBroadcast;
-    }
-
-    /**
-     * If there is a deferred broadcast that is being sent to an alarm target, return
-     * that one.  If there's no deferred alarm target broadcast but there is one
-     * that has reached the end of its deferral, return that.
-     *
-     * This stages the broadcast internally until it is retired, and returns that
-     * staged record if this is called repeatedly, until retireBroadcast(r) is called.
-     */
-    public BroadcastRecord getNextBroadcastLocked(final long now) {
-        if (mCurrentBroadcast != null) {
-            return mCurrentBroadcast;
-        }
-
-        BroadcastRecord next = null;
-
-        // Alarms in flight take precedence over everything else.  This queue
-        // will be non-empty only when the relevant policy is in force, but if
-        // policy has changed on the fly we still need to drain this before we
-        // settle into the legacy behavior.
-        if (!mAlarmQueue.isEmpty()) {
-            next = mAlarmQueue.remove(0);
-        }
-
-        // Next in precedence are deferred BOOT_COMPLETED broadcasts
-        if (next == null) {
-            next = dequeueDeferredBootCompletedBroadcast();
-        }
-
-        // Alarm-related deferrals are next in precedence...
-        if (next == null && !mAlarmDeferrals.isEmpty()) {
-            next = popLocked(mAlarmDeferrals);
-            if (DEBUG_BROADCAST_DEFERRAL && next != null) {
-                Slog.i(TAG, "Next broadcast from alarm targets: " + next);
-            }
-        }
-
-        final boolean someQueued = !mOrderedBroadcasts.isEmpty();
-
-        if (next == null && !mDeferredBroadcasts.isEmpty()) {
-            // A this point we're going to deliver either:
-            // 1. the next "overdue" deferral; or
-            // 2. the next ordinary ordered broadcast; *or*
-            // 3. the next not-yet-overdue deferral.
-
-            for (int i = 0; i < mDeferredBroadcasts.size(); i++) {
-                Deferrals d = mDeferredBroadcasts.get(i);
-                if (now < d.deferUntil && someQueued) {
-                    // stop looking when we haven't hit the next time-out boundary
-                    // but only if we have un-deferred broadcasts waiting,
-                    // otherwise we can deliver whatever deferred broadcast
-                    // is next available.
-                    break;
-                }
-
-                if (d.broadcasts.size() > 0) {
-                    next = d.broadcasts.remove(0);
-                    // apply deferral-interval decay policy and move this uid's
-                    // deferred broadcasts down in the delivery queue accordingly
-                    mDeferredBroadcasts.remove(i); // already 'd'
-                    d.deferredBy = calculateDeferral(d.deferredBy);
-                    d.deferUntil += d.deferredBy;
-                    insertLocked(mDeferredBroadcasts, d);
-                    if (DEBUG_BROADCAST_DEFERRAL) {
-                        Slog.i(TAG, "Next broadcast from deferrals " + next
-                                + ", deferUntil now " + d.deferUntil);
-                    }
-                    break;
-                }
-            }
-        }
-
-        if (next == null && someQueued) {
-            next = mOrderedBroadcasts.remove(0);
-            if (DEBUG_BROADCAST_DEFERRAL) {
-                Slog.i(TAG, "Next broadcast from main queue: " + next);
-            }
-        }
-
-        mCurrentBroadcast = next;
-        return next;
-    }
-
-    /**
-     * Called after the broadcast queue finishes processing the currently
-     * active broadcast (obtained by calling getNextBroadcastLocked()).
-     */
-    public void retireBroadcastLocked(final BroadcastRecord r) {
-        // ERROR if 'r' is not the active broadcast
-        if (r != mCurrentBroadcast) {
-            Slog.wtf(TAG, "Retiring broadcast " + r
-                    + " doesn't match current outgoing " + mCurrentBroadcast);
-        }
-        mCurrentBroadcast = null;
-    }
-
-    /**
-     * Called prior to broadcast dispatch to check whether the intended
-     * recipient is currently subject to deferral policy.
-     */
-    public boolean isDeferringLocked(final int uid) {
-        Deferrals d = findUidLocked(uid);
-        if (d != null && d.broadcasts.isEmpty()) {
-            // once we've caught up with deferred broadcasts to this uid
-            // and time has advanced sufficiently that we wouldn't be
-            // deferring newly-enqueued ones, we're back to normal policy.
-            if (SystemClock.uptimeMillis() >= d.deferUntil) {
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.i(TAG, "No longer deferring broadcasts to uid " + d.uid);
-                }
-                removeDeferral(d);
-                return false;
-            }
-        }
-        return (d != null);
-    }
-
-    /**
-     * Defer broadcasts for the given app.  If 'br' is non-null, this also makes
-     * sure that broadcast record is enqueued as the next upcoming broadcast for
-     * the app.
-     */
-    public void startDeferring(final int uid) {
-        synchronized (mLock) {
-            Deferrals d = findUidLocked(uid);
-
-            // If we're not yet tracking this app, set up that bookkeeping
-            if (d == null) {
-                // Start a new deferral
-                final long now = SystemClock.uptimeMillis();
-                d = new Deferrals(uid,
-                        now,
-                        mConstants.DEFERRAL,
-                        mAlarmUids.get(uid, 0));
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.i(TAG, "Now deferring broadcasts to " + uid
-                            + " until " + d.deferUntil);
-                }
-                // where it goes depends on whether it is coming into an alarm-related situation
-                if (d.alarmCount == 0) {
-                    // common case, put it in the ordinary priority queue
-                    insertLocked(mDeferredBroadcasts, d);
-                    scheduleDeferralCheckLocked(true);
-                } else {
-                    // alarm-related: strict order-encountered
-                    mAlarmDeferrals.add(d);
-                }
-            } else {
-                // We're already deferring, but something was slow again.  Reset the
-                // deferral decay progression.
-                d.deferredBy = mConstants.DEFERRAL;
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.i(TAG, "Uid " + uid + " slow again, deferral interval reset to "
-                            + d.deferredBy);
-                }
-            }
-        }
-    }
-
-    /**
-     * Key entry point when a broadcast about to be delivered is instead
-     * set aside for deferred delivery
-     */
-    public void addDeferredBroadcast(final int uid, BroadcastRecord br) {
-        if (DEBUG_BROADCAST_DEFERRAL) {
-            Slog.i(TAG, "Enqueuing deferred broadcast " + br);
-        }
-        synchronized (mLock) {
-            Deferrals d = findUidLocked(uid);
-            if (d == null) {
-                Slog.wtf(TAG, "Adding deferred broadcast but not tracking " + uid);
-            } else {
-                if (br == null) {
-                    Slog.wtf(TAG, "Deferring null broadcast to " + uid);
-                } else {
-                    br.deferred = true;
-                    d.add(br);
-                }
-            }
-        }
-    }
-
-    /**
-     * When there are deferred broadcasts, we need to make sure to recheck the
-     * dispatch queue when they come due.  Alarm-sensitive deferrals get dispatched
-     * aggressively, so we only need to use the ordinary deferrals timing to figure
-     * out when to recheck.
-     */
-    public void scheduleDeferralCheckLocked(boolean force) {
-        if ((force || !mRecheckScheduled) && !mDeferredBroadcasts.isEmpty()) {
-            final Deferrals d = mDeferredBroadcasts.get(0);
-            if (!d.broadcasts.isEmpty()) {
-                mHandler.removeCallbacks(mScheduleRunnable);
-                mHandler.postAtTime(mScheduleRunnable, d.deferUntil);
-                mRecheckScheduled = true;
-                if (DEBUG_BROADCAST_DEFERRAL) {
-                    Slog.i(TAG, "Scheduling deferred broadcast recheck at " + d.deferUntil);
-                }
-            }
-        }
-    }
-
-    /**
-     * Cancel all current deferrals; that is, make all currently-deferred broadcasts
-     * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
-     */
-    public void cancelDeferralsLocked() {
-        zeroDeferralTimes(mAlarmDeferrals);
-        zeroDeferralTimes(mDeferredBroadcasts);
-    }
-
-    private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
-        final int num = list.size();
-        for (int i = 0; i < num; i++) {
-            Deferrals d = list.get(i);
-            // Safe to do this in-place because it won't break ordering
-            d.deferUntil = d.deferredBy = 0;
-        }
-    }
-
-    // ----------------------------------
-
-    /**
-     * If broadcasts to this uid are being deferred, find the deferrals record about it.
-     * @return null if this uid's broadcasts are not being deferred
-     */
-    private Deferrals findUidLocked(final int uid) {
-        // The common case is that they it isn't also an alarm target...
-        Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
-        // ...but if not there, also check alarm-prioritized deferrals
-        if (d == null) {
-            d = findUidLocked(uid, mAlarmDeferrals);
-        }
-        return d;
-    }
-
-    /**
-     * Remove the given deferral record from whichever queue it might be in at present
-     * @return true if the deferral was in fact found, false if this made no changes
-     */
-    private boolean removeDeferral(Deferrals d) {
-        boolean didRemove = mDeferredBroadcasts.remove(d);
-        if (!didRemove) {
-            didRemove = mAlarmDeferrals.remove(d);
-        }
-        return didRemove;
-    }
-
-    /**
-     * Find the deferrals record for the given uid in the given list
-     */
-    private static Deferrals findUidLocked(final int uid, ArrayList<Deferrals> list) {
-        final int numElements = list.size();
-        for (int i = 0; i < numElements; i++) {
-            Deferrals d = list.get(i);
-            if (uid == d.uid) {
-                return d;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Pop the next broadcast record from the head of the given deferrals list,
-     * if one exists.
-     */
-    private static BroadcastRecord popLocked(ArrayList<Deferrals> list) {
-        final Deferrals d = list.get(0);
-        return d.broadcasts.isEmpty() ? null : d.broadcasts.remove(0);
-    }
-
-    /**
-     * Insert the given Deferrals into the priority queue, sorted by defer-until milestone
-     */
-    private static void insertLocked(ArrayList<Deferrals> list, Deferrals d) {
-        // Simple linear search is appropriate here because we expect to
-        // have very few entries in the deferral lists (i.e. very few badly-
-        // behaving apps currently facing deferral)
-        int i;
-        final int numElements = list.size();
-        for (i = 0; i < numElements; i++) {
-            if (d.deferUntil < list.get(i).deferUntil) {
-                break;
-            }
-        }
-        list.add(i, d);
-    }
-
-    /**
-     * Calculate a new deferral time based on the previous time.  This should decay
-     * toward zero, though a small nonzero floor is an option.
-     */
-    private long calculateDeferral(long previous) {
-        return Math.max(mConstants.DEFERRAL_FLOOR,
-                (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR));
-    }
-
-    // ----------------------------------
-
-    @NeverCompile
-    boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
-            SimpleDateFormat sdf) {
-        final Dumper dumper = new Dumper(pw, queueName, dumpPackage, sdf);
-        boolean printed = false;
-
-        dumper.setHeading("Currently in flight");
-        dumper.setLabel("In-Flight Ordered Broadcast");
-        if (mCurrentBroadcast != null) {
-            dumper.dump(mCurrentBroadcast);
-        } else {
-            pw.println("  (null)");
-        }
-        printed |= dumper.didPrint();
-
-        dumper.setHeading("Active alarm broadcasts");
-        dumper.setLabel("Active Alarm Broadcast");
-        for (BroadcastRecord br : mAlarmQueue) {
-            dumper.dump(br);
-        }
-        printed |= dumper.didPrint();
-
-        dumper.setHeading("Active ordered broadcasts");
-        dumper.setLabel("Active Ordered Broadcast");
-        for (Deferrals d : mAlarmDeferrals) {
-            d.dumpLocked(dumper);
-        }
-        for (BroadcastRecord br : mOrderedBroadcasts) {
-            dumper.dump(br);
-        }
-        printed |= dumper.didPrint();
-
-        dumper.setHeading("Deferred ordered broadcasts");
-        dumper.setLabel("Deferred Ordered Broadcast");
-        for (Deferrals d : mDeferredBroadcasts) {
-            d.dumpLocked(dumper);
-        }
-        printed |= dumper.didPrint();
-
-        dumper.setHeading("Deferred LOCKED_BOOT_COMPLETED broadcasts");
-        dumper.setLabel("Deferred LOCKED_BOOT_COMPLETED Broadcast");
-        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
-            mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_LOCKED_BOOT_COMPLETED);
-        }
-        printed |= dumper.didPrint();
-
-        dumper.setHeading("Deferred BOOT_COMPLETED broadcasts");
-        dumper.setLabel("Deferred BOOT_COMPLETED Broadcast");
-        for (int i = 0, size = mUser2Deferred.size(); i < size; i++) {
-            mUser2Deferred.valueAt(i).dump(dumper, Intent.ACTION_BOOT_COMPLETED);
-        }
-        printed |= dumper.didPrint();
-
-        return printed;
-    }
-}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
deleted file mode 100644
index 3c56752..0000000
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ /dev/null
@@ -1,1983 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
-import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
-import static android.text.TextUtils.formatSimple;
-
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
-import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
-import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ApplicationExitInfo;
-import android.app.BroadcastOptions;
-import android.app.IApplicationThread;
-import android.app.usage.UsageEvents.Event;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.IIntentReceiver;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager.ReasonCode;
-import android.os.PowerExemptionManager.TempAllowListType;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.IndentingPrintWriter;
-import android.util.Slog;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.os.TimeoutRecord;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.LocalServices;
-import com.android.server.pm.UserJourneyLogger;
-import com.android.server.pm.UserManagerInternal;
-
-import dalvik.annotation.optimization.NeverCompile;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Set;
-import java.util.function.BooleanSupplier;
-
-/**
- * BROADCASTS
- *
- * We keep three broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts, and one to
- * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
- */
-public class BroadcastQueueImpl extends BroadcastQueue {
-    private static final String TAG_MU = TAG + POSTFIX_MU;
-    private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
-
-    final BroadcastConstants mConstants;
-
-    /**
-     * If true, we can delay broadcasts while waiting services to finish in the previous
-     * receiver's process.
-     */
-    final boolean mDelayBehindServices;
-
-    final int mSchedGroup;
-
-    /**
-     * Lists of all active broadcasts that are to be executed immediately
-     * (without waiting for another broadcast to finish).  Currently this only
-     * contains broadcasts to registered receivers, to avoid spinning up
-     * a bunch of processes to execute IntentReceiver components.  Background-
-     * and foreground-priority broadcasts are queued separately.
-     */
-    final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
-
-    /**
-     * Tracking of the ordered broadcast queue, including deferral policy and alarm
-     * prioritization.
-     */
-    final BroadcastDispatcher mDispatcher;
-
-    /**
-     * Refcounting for completion callbacks of split/deferred broadcasts.  The key
-     * is an opaque integer token assigned lazily when a broadcast is first split
-     * into multiple BroadcastRecord objects.
-     */
-    final SparseIntArray mSplitRefcounts = new SparseIntArray();
-    private int mNextToken = 0;
-
-    /**
-     * Set when we current have a BROADCAST_INTENT_MSG in flight.
-     */
-    boolean mBroadcastsScheduled = false;
-
-    /**
-     * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
-     */
-    boolean mPendingBroadcastTimeoutMessage;
-
-    /**
-     * Intent broadcasts that we have tried to start, but are
-     * waiting for the application's process to be created.  We only
-     * need one per scheduling class (instead of a list) because we always
-     * process broadcasts one at a time, so no others can be started while
-     * waiting for this one.
-     */
-    BroadcastRecord mPendingBroadcast = null;
-
-    /**
-     * The receiver index that is pending, to restart the broadcast if needed.
-     */
-    int mPendingBroadcastRecvIndex;
-
-    static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
-    static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
-
-    // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
-    boolean mLogLatencyMetrics = true;
-
-    final BroadcastHandler mHandler;
-
-    private final class BroadcastHandler extends Handler {
-        public BroadcastHandler(Looper looper) {
-            super(looper, null);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case BROADCAST_INTENT_MSG: {
-                    if (DEBUG_BROADCAST) Slog.v(
-                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
-                            + mQueueName + "]");
-                    processNextBroadcast(true);
-                } break;
-                case BROADCAST_TIMEOUT_MSG: {
-                    synchronized (mService) {
-                        broadcastTimeoutLocked(true);
-                    }
-                } break;
-            }
-        }
-    }
-
-    BroadcastQueueImpl(ActivityManagerService service, Handler handler,
-            String name, BroadcastConstants constants, boolean allowDelayBehindServices,
-            int schedGroup) {
-        this(service, handler, name, constants, new BroadcastSkipPolicy(service),
-                new BroadcastHistory(constants), allowDelayBehindServices, schedGroup);
-    }
-
-    BroadcastQueueImpl(ActivityManagerService service, Handler handler,
-            String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy,
-            BroadcastHistory history, boolean allowDelayBehindServices, int schedGroup) {
-        super(service, handler, name, skipPolicy, history);
-        mHandler = new BroadcastHandler(handler.getLooper());
-        mConstants = constants;
-        mDelayBehindServices = allowDelayBehindServices;
-        mSchedGroup = schedGroup;
-        mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
-    }
-
-    public void start(ContentResolver resolver) {
-        mDispatcher.start();
-        mConstants.startObserving(mHandler, resolver);
-    }
-
-    public boolean isDelayBehindServices() {
-        return mDelayBehindServices;
-    }
-
-    public BroadcastRecord getPendingBroadcastLocked() {
-        return mPendingBroadcast;
-    }
-
-    public BroadcastRecord getActiveBroadcastLocked() {
-        return mDispatcher.getActiveBroadcastLocked();
-    }
-
-    public int getPreferredSchedulingGroupLocked(ProcessRecord app) {
-        final BroadcastRecord active = getActiveBroadcastLocked();
-        if (active != null && active.curApp == app) {
-            return mSchedGroup;
-        }
-        final BroadcastRecord pending = getPendingBroadcastLocked();
-        if (pending != null && pending.curApp == app) {
-            return mSchedGroup;
-        }
-        return ProcessList.SCHED_GROUP_UNDEFINED;
-    }
-
-    public void enqueueBroadcastLocked(BroadcastRecord r) {
-        r.applySingletonPolicy(mService);
-
-        final boolean replacePending = (r.intent.getFlags()
-                & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
-
-        // Ordered broadcasts obviously need to be dispatched in serial order,
-        // but this implementation expects all manifest receivers to also be
-        // dispatched in a serial fashion
-        boolean serialDispatch = r.ordered;
-        if (!serialDispatch) {
-            final int N = (r.receivers != null) ? r.receivers.size() : 0;
-            for (int i = 0; i < N; i++) {
-                if (r.receivers.get(i) instanceof ResolveInfo) {
-                    serialDispatch = true;
-                    break;
-                }
-            }
-        }
-
-        if (serialDispatch) {
-            final BroadcastRecord oldRecord =
-                    replacePending ? replaceOrderedBroadcastLocked(r) : null;
-            if (oldRecord != null) {
-                // Replaced, fire the result-to receiver.
-                if (oldRecord.resultTo != null) {
-                    try {
-                        oldRecord.mIsReceiverAppRunning = true;
-                        performReceiveLocked(oldRecord, oldRecord.resultToApp, oldRecord.resultTo,
-                                oldRecord.intent,
-                                Activity.RESULT_CANCELED, null, null,
-                                false, false, oldRecord.shareIdentity, oldRecord.userId,
-                                oldRecord.callingUid, r.callingUid, r.callerPackage,
-                                SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0, 0,
-                                oldRecord.resultToApp != null
-                                        ? oldRecord.resultToApp.mState.getCurProcState()
-                                        : ActivityManager.PROCESS_STATE_UNKNOWN);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failure ["
-                                + mQueueName + "] sending broadcast result of "
-                                + oldRecord.intent, e);
-
-                    }
-                }
-            } else {
-                enqueueOrderedBroadcastLocked(r);
-                scheduleBroadcastsLocked();
-            }
-        } else {
-            final boolean replaced = replacePending
-                    && (replaceParallelBroadcastLocked(r) != null);
-            // Note: We assume resultTo is null for non-ordered broadcasts.
-            if (!replaced) {
-                enqueueParallelBroadcastLocked(r);
-                scheduleBroadcastsLocked();
-            }
-        }
-    }
-
-    public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
-        r.enqueueClockTime = System.currentTimeMillis();
-        r.enqueueTime = SystemClock.uptimeMillis();
-        r.enqueueRealTime = SystemClock.elapsedRealtime();
-        mParallelBroadcasts.add(r);
-        enqueueBroadcastHelper(r);
-    }
-
-    public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
-        r.enqueueClockTime = System.currentTimeMillis();
-        r.enqueueTime = SystemClock.uptimeMillis();
-        r.enqueueRealTime = SystemClock.elapsedRealtime();
-        mDispatcher.enqueueOrderedBroadcastLocked(r);
-        enqueueBroadcastHelper(r);
-    }
-
-    /**
-     * Don't call this method directly; call enqueueParallelBroadcastLocked or
-     * enqueueOrderedBroadcastLocked.
-     */
-    private void enqueueBroadcastHelper(BroadcastRecord r) {
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
-                System.identityHashCode(r));
-        }
-    }
-
-    /**
-     * Find the same intent from queued parallel broadcast, replace with a new one and return
-     * the old one.
-     */
-    public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
-        return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
-    }
-
-    /**
-     * Find the same intent from queued ordered broadcast, replace with a new one and return
-     * the old one.
-     */
-    public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
-        return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
-    }
-
-    private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
-            BroadcastRecord r, String typeForLogging) {
-        final Intent intent = r.intent;
-        for (int i = queue.size() - 1; i >= 0; i--) {
-            final BroadcastRecord old = queue.get(i);
-            if (old.userId == r.userId && intent.filterEquals(old.intent)) {
-                if (DEBUG_BROADCAST) {
-                    Slog.v(TAG_BROADCAST, "***** DROPPING "
-                            + typeForLogging + " [" + mQueueName + "]: " + intent);
-                }
-                queue.set(i, r);
-                return old;
-            }
-        }
-        return null;
-    }
-
-    private final void processCurBroadcastLocked(BroadcastRecord r,
-            ProcessRecord app) throws RemoteException {
-        if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                "Process cur broadcast " + r + " for app " + app);
-        final IApplicationThread thread = app.getThread();
-        if (thread == null) {
-            throw new RemoteException();
-        }
-        if (app.isInFullBackup()) {
-            skipReceiverLocked(r);
-            return;
-        }
-
-        r.curApp = app;
-        r.curAppLastProcessState = app.mState.getCurProcState();
-        final ProcessReceiverRecord prr = app.mReceivers;
-        prr.addCurReceiver(r);
-        app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        // Don't bump its LRU position if it's in the background restricted.
-        if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
-                < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
-            mService.updateLruProcessLocked(app, false, null);
-        }
-        // Make sure the oom adj score is updated before delivering the broadcast.
-        // Force an update, even if there are other pending requests, overall it still saves time,
-        // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
-        mService.enqueueOomAdjTargetLocked(app);
-        mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
-
-        // Tell the application to launch this receiver.
-        maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
-        r.intent.setComponent(r.curComponent);
-
-        // See if we need to delay the freezer based on BroadcastOptions
-        if (r.options != null
-                && r.options.getTemporaryAppAllowlistDuration() > 0
-                && r.options.getTemporaryAppAllowlistType()
-                    == TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
-                    CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,
-                    r.options.getTemporaryAppAllowlistDuration());
-        }
-
-        boolean started = false;
-        try {
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
-                    "Delivering to component " + r.curComponent
-                    + ": " + r);
-            mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
-                                      PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
-            final boolean assumeDelivered = false;
-            thread.scheduleReceiver(
-                    prepareReceiverIntent(r.intent, r.curFilteredExtras),
-                    r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
-                    r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
-                    r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                    app.mState.getReportedProcState(),
-                    r.shareIdentity ? r.callerPackage : null);
-            if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                    "Process cur broadcast " + r + " DELIVERED for app " + app);
-            started = true;
-        } finally {
-            if (!started) {
-                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                        "Process cur broadcast " + r + ": NOT STARTED!");
-                r.curApp = null;
-                r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN;
-                prr.removeCurReceiver(r);
-            }
-        }
-
-        // if something bad happens here, launch the app and try again
-        if (app.isKilled()) {
-            throw new RemoteException("app gets killed during broadcasting");
-        }
-    }
-
-    /**
-     * Called by ActivityManagerService to notify that the uid has process started, if there is any
-     * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
-     * @param uid
-     */
-    public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
-        mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
-        scheduleBroadcastsLocked();
-    }
-
-    public boolean onApplicationAttachedLocked(ProcessRecord app)
-            throws BroadcastDeliveryFailedException {
-        updateUidReadyForBootCompletedBroadcastLocked(app.uid);
-
-        if (mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
-            return sendPendingBroadcastsLocked(app);
-        } else {
-            return false;
-        }
-    }
-
-    public void onApplicationTimeoutLocked(ProcessRecord app) {
-        skipCurrentOrPendingReceiverLocked(app);
-    }
-
-    public void onApplicationProblemLocked(ProcessRecord app) {
-        skipCurrentOrPendingReceiverLocked(app);
-    }
-
-    public void onApplicationCleanupLocked(ProcessRecord app) {
-        skipCurrentOrPendingReceiverLocked(app);
-    }
-
-    public void onProcessFreezableChangedLocked(ProcessRecord app) {
-        // Not supported; ignore
-    }
-
-    public boolean sendPendingBroadcastsLocked(ProcessRecord app)
-            throws BroadcastDeliveryFailedException {
-        boolean didSomething = false;
-        final BroadcastRecord br = mPendingBroadcast;
-        if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
-            if (br.curApp != app) {
-                Slog.e(TAG, "App mismatch when sending pending broadcast to "
-                        + app.processName + ", intended target is " + br.curApp.processName);
-                return false;
-            }
-            try {
-                mPendingBroadcast = null;
-                br.mIsReceiverAppRunning = false;
-                processCurBroadcastLocked(br, app);
-                didSomething = true;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception in new application when starting receiver "
-                        + br.curComponent.flattenToShortString(), e);
-                logBroadcastReceiverDiscardLocked(br);
-                finishReceiverLocked(br, br.resultCode, br.resultData,
-                        br.resultExtras, br.resultAbort, false);
-                scheduleBroadcastsLocked();
-                // We need to reset the state if we failed to start the receiver.
-                br.state = BroadcastRecord.IDLE;
-                throw new BroadcastDeliveryFailedException(e);
-            }
-        }
-        return didSomething;
-    }
-
-    // Skip the current receiver, if any, that is in flight to the given process
-    public boolean skipCurrentOrPendingReceiverLocked(ProcessRecord app) {
-        BroadcastRecord r = null;
-        final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
-        if (curActive != null && curActive.curApp == app) {
-            // confirmed: the current active broadcast is to the given app
-            r = curActive;
-        }
-
-        // If the current active broadcast isn't this BUT we're waiting for
-        // mPendingBroadcast to spin up the target app, that's what we use.
-        if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
-            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
-                    "[" + mQueueName + "] skip & discard pending app " + r);
-            r = mPendingBroadcast;
-        }
-
-        if (r != null) {
-            skipReceiverLocked(r);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private void skipReceiverLocked(BroadcastRecord r) {
-        logBroadcastReceiverDiscardLocked(r);
-        finishReceiverLocked(r, r.resultCode, r.resultData,
-                r.resultExtras, r.resultAbort, false);
-        scheduleBroadcastsLocked();
-    }
-
-    public void scheduleBroadcastsLocked() {
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
-                + mQueueName + "]: current="
-                + mBroadcastsScheduled);
-
-        if (mBroadcastsScheduled) {
-            return;
-        }
-        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
-        mBroadcastsScheduled = true;
-    }
-
-    public BroadcastRecord getMatchingOrderedReceiver(ProcessRecord app) {
-        BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
-        if (br == null) {
-            Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
-                    + "] no active broadcast");
-            return null;
-        }
-        if (br.curApp != app) {
-            Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
-                    + "] active broadcast " + br.curApp + " doesn't match " + app);
-            return null;
-        }
-        return br;
-    }
-
-    // > 0 only, no worry about "eventual" recycling
-    private int nextSplitTokenLocked() {
-        int next = mNextToken + 1;
-        if (next <= 0) {
-            next = 1;
-        }
-        mNextToken = next;
-        return next;
-    }
-
-    private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
-        // the receiver had run for less than allowed bg activity start timeout,
-        // so allow the process to still start activities from bg for some more time
-        String msgToken = (app.toShortString() + r.toString()).intern();
-        // first, if there exists a past scheduled request to remove this token, drop
-        // that request - we don't want the token to be swept from under our feet...
-        mHandler.removeCallbacksAndMessages(msgToken);
-        // ...then schedule the removal of the token after the extended timeout
-        mHandler.postAtTime(() -> {
-            synchronized (mService) {
-                app.removeBackgroundStartPrivileges(r);
-            }
-        }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
-    }
-
-    public boolean finishReceiverLocked(ProcessRecord app, int resultCode,
-            String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
-        final BroadcastRecord r = getMatchingOrderedReceiver(app);
-        if (r != null) {
-            return finishReceiverLocked(r, resultCode,
-                    resultData, resultExtras, resultAbort, waitForServices);
-        } else {
-            return false;
-        }
-    }
-
-    public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
-            String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
-        final int state = r.state;
-        final ActivityInfo receiver = r.curReceiver;
-        final long finishTime = SystemClock.uptimeMillis();
-        final long elapsed = finishTime - r.receiverTime;
-        r.state = BroadcastRecord.IDLE;
-        final int curIndex = r.nextReceiver - 1;
-
-        final int packageState = r.mWasReceiverAppStopped
-                ? SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
-                : SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
-
-        if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
-            final Object curReceiver = r.receivers.get(curIndex);
-            FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
-                    r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
-                    r.intent.getAction(),
-                    curReceiver instanceof BroadcastFilter
-                    ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
-                    : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
-                    r.mIsReceiverAppRunning
-                    ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
-                    : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
-                    r.dispatchTime - r.enqueueTime,
-                    r.receiverTime - r.dispatchTime,
-                    finishTime - r.receiverTime,
-                    packageState,
-                    r.curApp.info.packageName,
-                    r.callerPackage,
-                    r.calculateTypeForLogging(),
-                    r.getDeliveryGroupPolicy(),
-                    r.intent.getFlags(),
-                    BroadcastRecord.getReceiverPriority(curReceiver),
-                    r.callerProcState,
-                    r.curAppLastProcessState);
-        }
-        if (state == BroadcastRecord.IDLE) {
-            Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
-        }
-        if (r.mBackgroundStartPrivileges.allowsAny() && r.curApp != null) {
-            if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
-                // if the receiver has run for more than allowed bg activity start timeout,
-                // just remove the token for this process now and we're done
-                r.curApp.removeBackgroundStartPrivileges(r);
-            } else {
-                // It gets more time; post the removal to happen at the appropriate moment
-                postActivityStartTokenRemoval(r.curApp, r);
-            }
-        }
-        // If we're abandoning this broadcast before any receivers were actually spun up,
-        // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
-        if (r.nextReceiver > 0) {
-            r.terminalTime[r.nextReceiver - 1] = finishTime;
-        }
-
-        // if this receiver was slow, impose deferral policy on the app.  This will kick in
-        // when processNextBroadcastLocked() next finds this uid as a receiver identity.
-        if (!r.timeoutExempt) {
-            // r.curApp can be null if finish has raced with process death - benign
-            // edge case, and we just ignore it because we're already cleaning up
-            // as expected.
-            if (r.curApp != null
-                    && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
-                // Core system packages are exempt from deferral policy
-                if (!UserHandle.isCore(r.curApp.uid)) {
-                    if (DEBUG_BROADCAST_DEFERRAL) {
-                        Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
-                                + " was slow: " + receiver + " br=" + r);
-                    }
-                    mDispatcher.startDeferring(r.curApp.uid);
-                } else {
-                    if (DEBUG_BROADCAST_DEFERRAL) {
-                        Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
-                                + " receiver was slow but not deferring: "
-                                + receiver + " br=" + r);
-                    }
-                }
-            }
-        } else {
-            if (DEBUG_BROADCAST_DEFERRAL) {
-                Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
-                        + " is exempt from deferral policy");
-            }
-        }
-
-        r.intent.setComponent(null);
-        if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
-            r.curApp.mReceivers.removeCurReceiver(r);
-            mService.enqueueOomAdjTargetLocked(r.curApp);
-        }
-        if (r.curFilter != null) {
-            r.curFilter.receiverList.curBroadcast = null;
-        }
-        r.curFilter = null;
-        r.curReceiver = null;
-        r.curApp = null;
-        r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN;
-        r.curFilteredExtras = null;
-        r.mWasReceiverAppStopped = false;
-        mPendingBroadcast = null;
-
-        r.resultCode = resultCode;
-        r.resultData = resultData;
-        r.resultExtras = resultExtras;
-        if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
-            r.resultAbort = resultAbort;
-        } else {
-            r.resultAbort = false;
-        }
-
-        // If we want to wait behind services *AND* we're finishing the head/
-        // active broadcast on its queue
-        if (waitForServices && r.curComponent != null && r.queue.isDelayBehindServices()
-                && ((BroadcastQueueImpl) r.queue).getActiveBroadcastLocked() == r) {
-            ActivityInfo nextReceiver;
-            if (r.nextReceiver < r.receivers.size()) {
-                Object obj = r.receivers.get(r.nextReceiver);
-                nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
-            } else {
-                nextReceiver = null;
-            }
-            // Don't do this if the next receive is in the same process as the current one.
-            if (receiver == null || nextReceiver == null
-                    || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
-                    || !receiver.processName.equals(nextReceiver.processName)) {
-                // In this case, we are ready to process the next receiver for the current broadcast,
-                // but are on a queue that would like to wait for services to finish before moving
-                // on.  If there are background services currently starting, then we will go into a
-                // special state where we hold off on continuing this broadcast until they are done.
-                if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
-                    Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
-                    r.state = BroadcastRecord.WAITING_SERVICES;
-                    return false;
-                }
-            }
-        }
-
-        r.curComponent = null;
-
-        // We will process the next receiver right now if this is finishing
-        // an app receiver (which is always asynchronous) or after we have
-        // come back from calling a receiver.
-        final boolean doNext = (state == BroadcastRecord.APP_RECEIVE)
-                || (state == BroadcastRecord.CALL_DONE_RECEIVE);
-        if (doNext) {
-            processNextBroadcastLocked(/* fromMsg= */ false, /* skipOomAdj= */ true);
-        }
-        return doNext;
-    }
-
-    public void backgroundServicesFinishedLocked(int userId) {
-        BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
-        if (br != null) {
-            if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
-                Slog.i(TAG, "Resuming delayed broadcast");
-                br.curComponent = null;
-                br.state = BroadcastRecord.IDLE;
-                processNextBroadcastLocked(false, false);
-            }
-        }
-    }
-
-    public void performReceiveLocked(BroadcastRecord r, ProcessRecord app, IIntentReceiver receiver,
-            Intent intent, int resultCode, String data, Bundle extras,
-            boolean ordered, boolean sticky, boolean shareIdentity, int sendingUser,
-            int receiverUid, int callingUid, String callingPackage,
-            long dispatchDelay, long receiveDelay, int priority,
-            int receiverProcessState) throws RemoteException {
-        // If the broadcaster opted-in to sharing their identity, then expose package visibility for
-        // the receiver.
-        if (shareIdentity) {
-            mService.mPackageManagerInt.grantImplicitAccess(sendingUser, intent,
-                    UserHandle.getAppId(receiverUid), callingUid, true);
-        }
-        // Send the intent to the receiver asynchronously using one-way binder calls.
-        if (app != null) {
-            final IApplicationThread thread = app.getThread();
-            if (thread != null) {
-                // If we have an app thread, do the call through that so it is
-                // correctly ordered with other one-way calls.
-                try {
-                    final boolean assumeDelivered = !ordered;
-                    thread.scheduleRegisteredReceiver(
-                            receiver, intent, resultCode,
-                            data, extras, ordered, sticky, assumeDelivered, sendingUser,
-                            app.mState.getReportedProcState(),
-                            shareIdentity ? callingUid : Process.INVALID_UID,
-                            shareIdentity ? callingPackage : null);
-                } catch (RemoteException ex) {
-                    // Failed to call into the process. It's either dying or wedged. Kill it gently.
-                    synchronized (mService) {
-                        final String msg = "Failed to schedule " + intent + " to " + receiver
-                                + " via " + app + ": " + ex;
-                        Slog.w(TAG, msg);
-                        app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
-                                ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
-                    }
-                    throw ex;
-                }
-            } else {
-                // Application has died. Receiver doesn't exist.
-                throw new RemoteException("app.thread must not be null");
-            }
-        } else {
-            receiver.performReceive(intent, resultCode, data, extras, ordered,
-                    sticky, sendingUser);
-        }
-        if (!ordered) {
-            FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
-                    receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
-                    callingUid == -1 ? Process.SYSTEM_UID : callingUid,
-                    intent.getAction(),
-                    BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
-                    BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
-                    dispatchDelay, receiveDelay, 0 /* finish_delay */,
-                    SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
-                    app != null ? app.info.packageName : null, callingPackage,
-                    r.calculateTypeForLogging(), r.getDeliveryGroupPolicy(), r.intent.getFlags(),
-                    priority, r.callerProcState, receiverProcessState);
-        }
-    }
-
-    private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
-            BroadcastFilter filter, boolean ordered, int index) {
-        boolean skip = mSkipPolicy.shouldSkip(r, filter);
-
-        // Filter packages in the intent extras, skipping delivery if none of the packages is
-        // visible to the receiver.
-        Bundle filteredExtras = null;
-        if (!skip && r.filterExtrasForReceiver != null) {
-            final Bundle extras = r.intent.getExtras();
-            if (extras != null) {
-                filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
-                if (filteredExtras == null) {
-                    if (DEBUG_BROADCAST) {
-                        Slog.v(TAG, "Skipping delivery to "
-                                + filter.receiverList.app
-                                + " : receiver is filtered by the package visibility");
-                    }
-                    skip = true;
-                }
-            }
-        }
-
-        if (skip) {
-            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
-            return;
-        }
-
-        r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
-
-        // If this is not being sent as an ordered broadcast, then we
-        // don't want to touch the fields that keep track of the current
-        // state of ordered broadcasts.
-        if (ordered) {
-            r.curFilter = filter;
-            filter.receiverList.curBroadcast = r;
-            r.state = BroadcastRecord.CALL_IN_RECEIVE;
-            if (filter.receiverList.app != null) {
-                // Bump hosting application to no longer be in background
-                // scheduling class.  Note that we can't do that if there
-                // isn't an app...  but we can only be in that case for
-                // things that directly call the IActivityManager API, which
-                // are already core system stuff so don't matter for this.
-                r.curApp = filter.receiverList.app;
-                r.curAppLastProcessState = r.curApp.mState.getCurProcState();
-                filter.receiverList.app.mReceivers.addCurReceiver(r);
-                mService.enqueueOomAdjTargetLocked(r.curApp);
-                mService.updateOomAdjPendingTargetsLocked(
-                        OOM_ADJ_REASON_START_RECEIVER);
-            }
-        } else if (filter.receiverList.app != null) {
-            mService.mOomAdjuster.unfreezeTemporarily(filter.receiverList.app,
-                    CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
-        }
-
-        try {
-            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
-                    "Delivering to " + filter + " : " + r);
-            final boolean isInFullBackup = (filter.receiverList.app != null)
-                    && filter.receiverList.app.isInFullBackup();
-            final boolean isKilled = (filter.receiverList.app != null)
-                    && filter.receiverList.app.isKilled();
-            if (isInFullBackup || isKilled) {
-                // Skip delivery if full backup in progress
-                // If it's an ordered broadcast, we need to continue to the next receiver.
-                if (ordered) {
-                    skipReceiverLocked(r);
-                }
-            } else {
-                r.receiverTime = SystemClock.uptimeMillis();
-                r.scheduledTime[index] = r.receiverTime;
-                maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
-                maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
-                maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
-                performReceiveLocked(r, filter.receiverList.app, filter.receiverList.receiver,
-                        prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
-                        r.resultExtras, r.ordered, r.initialSticky, r.shareIdentity, r.userId,
-                        filter.receiverList.uid, r.callingUid, r.callerPackage,
-                        r.dispatchTime - r.enqueueTime,
-                        r.receiverTime - r.dispatchTime, filter.getPriority(),
-                        filter.receiverList.app != null
-                                ? filter.receiverList.app.mState.getCurProcState()
-                                : ActivityManager.PROCESS_STATE_UNKNOWN);
-                // parallel broadcasts are fire-and-forget, not bookended by a call to
-                // finishReceiverLocked(), so we manage their activity-start token here
-                if (filter.receiverList.app != null
-                        && r.mBackgroundStartPrivileges.allowsAny()
-                        && !r.ordered) {
-                    postActivityStartTokenRemoval(filter.receiverList.app, r);
-                }
-            }
-            if (ordered) {
-                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
-            // Clean up ProcessRecord state related to this broadcast attempt
-            if (filter.receiverList.app != null) {
-                filter.receiverList.app.removeBackgroundStartPrivileges(r);
-                if (ordered) {
-                    filter.receiverList.app.mReceivers.removeCurReceiver(r);
-                    // Something wrong, its oom adj could be downgraded, but not in a hurry.
-                    mService.enqueueOomAdjTargetLocked(r.curApp);
-                }
-            }
-            // And BroadcastRecord state related to ordered delivery, if appropriate
-            if (ordered) {
-                r.curFilter = null;
-                filter.receiverList.curBroadcast = null;
-            }
-        }
-    }
-
-    void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
-            @Nullable BroadcastOptions brOptions) {
-        if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
-            return;
-        }
-        long duration = brOptions.getTemporaryAppAllowlistDuration();
-        final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
-        final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
-        final String reason = brOptions.getTemporaryAppAllowlistReason();
-
-        if (duration > Integer.MAX_VALUE) {
-            duration = Integer.MAX_VALUE;
-        }
-        // XXX ideally we should pause the broadcast until everything behind this is done,
-        // or else we will likely start dispatching the broadcast before we have opened
-        // access to the app (there is a lot of asynchronicity behind this).  It is probably
-        // not that big a deal, however, because the main purpose here is to allow apps
-        // to hold wake locks, and they will be able to acquire their wake lock immediately
-        // it just won't be enabled until we get through this work.
-        StringBuilder b = new StringBuilder();
-        b.append("broadcast:");
-        UserHandle.formatUid(b, r.callingUid);
-        b.append(":");
-        if (r.intent.getAction() != null) {
-            b.append(r.intent.getAction());
-        } else if (r.intent.getComponent() != null) {
-            r.intent.getComponent().appendShortString(b);
-        } else if (r.intent.getData() != null) {
-            b.append(r.intent.getData());
-        }
-        b.append(",reason:");
-        b.append(reason);
-        if (DEBUG_BROADCAST) {
-            Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
-                    + " type=" + type + " : " + b.toString());
-        }
-
-        // Only add to temp allowlist if it's not the APP_FREEZING_DELAYED type. That will be
-        // handled when the broadcast is actually being scheduled on the app thread.
-        if (type != TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
-            mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
-                    r.callingUid);
-        }
-    }
-
-    private void processNextBroadcast(boolean fromMsg) {
-        synchronized (mService) {
-            processNextBroadcastLocked(fromMsg, false);
-        }
-    }
-
-    private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
-            @Nullable Bundle filteredExtras) {
-        final Intent intent = new Intent(originalIntent);
-        if (filteredExtras != null) {
-            intent.replaceExtras(filteredExtras);
-        }
-        return intent;
-    }
-
-    public void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
-        BroadcastRecord r;
-
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
-                + mQueueName + "]: "
-                + mParallelBroadcasts.size() + " parallel broadcasts; "
-                + mDispatcher.describeStateLocked());
-
-        mService.updateCpuStats();
-
-        if (fromMsg) {
-            mBroadcastsScheduled = false;
-        }
-
-        // First, deliver any non-serialized broadcasts right away.
-        while (mParallelBroadcasts.size() > 0) {
-            r = mParallelBroadcasts.remove(0);
-            r.dispatchTime = SystemClock.uptimeMillis();
-            r.dispatchRealTime = SystemClock.elapsedRealtime();
-            r.dispatchClockTime = System.currentTimeMillis();
-            r.mIsReceiverAppRunning = true;
-
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-                Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
-                    System.identityHashCode(r));
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
-                    System.identityHashCode(r));
-            }
-
-            final int N = r.receivers.size();
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
-                    + mQueueName + "] " + r);
-            for (int i=0; i<N; i++) {
-                Object target = r.receivers.get(i);
-                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                        "Delivering non-ordered on [" + mQueueName + "] to registered "
-                        + target + ": " + r);
-                deliverToRegisteredReceiverLocked(r,
-                        (BroadcastFilter) target, false, i);
-            }
-            addBroadcastToHistoryLocked(r);
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
-                    + mQueueName + "] " + r);
-        }
-
-        // Now take care of the next serialized one...
-
-        // If we are waiting for a process to come up to handle the next
-        // broadcast, then do nothing at this point.  Just in case, we
-        // check that the process we're waiting for still exists.
-        if (mPendingBroadcast != null) {
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
-                    "processNextBroadcast [" + mQueueName + "]: waiting for "
-                    + mPendingBroadcast.curApp);
-
-            boolean isDead;
-            if (mPendingBroadcast.curApp.getPid() > 0) {
-                synchronized (mService.mPidsSelfLocked) {
-                    ProcessRecord proc = mService.mPidsSelfLocked.get(
-                            mPendingBroadcast.curApp.getPid());
-                    isDead = proc == null || proc.mErrorState.isCrashing();
-                }
-            } else {
-                final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
-                        mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
-                isDead = proc == null || !proc.isPendingStart();
-            }
-            if (!isDead) {
-                // It's still alive, so keep waiting
-                return;
-            } else {
-                Slog.w(TAG, "pending app  ["
-                        + mQueueName + "]" + mPendingBroadcast.curApp
-                        + " died before responding to broadcast");
-                mPendingBroadcast.state = BroadcastRecord.IDLE;
-                mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
-                mPendingBroadcast = null;
-            }
-        }
-
-        boolean looped = false;
-
-        do {
-            final long now = SystemClock.uptimeMillis();
-            r = mDispatcher.getNextBroadcastLocked(now);
-
-            if (r == null) {
-                // No more broadcasts are deliverable right now, so all done!
-                mDispatcher.scheduleDeferralCheckLocked(false);
-                synchronized (mService.mAppProfiler.mProfilerLock) {
-                    mService.mAppProfiler.scheduleAppGcsLPf();
-                }
-                if (looped && !skipOomAdj) {
-                    // If we had finished the last ordered broadcast, then
-                    // make sure all processes have correct oom and sched
-                    // adjustments.
-                    mService.updateOomAdjPendingTargetsLocked(
-                            OOM_ADJ_REASON_START_RECEIVER);
-                }
-
-                // when we have no more ordered broadcast on this queue, stop logging
-                if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
-                    mLogLatencyMetrics = false;
-                }
-
-                return;
-            }
-
-            boolean forceReceive = false;
-
-            // Ensure that even if something goes awry with the timeout
-            // detection, we catch "hung" broadcasts here, discard them,
-            // and continue to make progress.
-            //
-            // This is only done if the system is ready so that early-stage receivers
-            // don't get executed with timeouts; and of course other timeout-
-            // exempt broadcasts are ignored.
-            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-            if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
-                if ((numReceivers > 0) &&
-                        (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
-                    Slog.w(TAG, "Hung broadcast ["
-                            + mQueueName + "] discarded after timeout failure:"
-                            + " now=" + now
-                            + " dispatchTime=" + r.dispatchTime
-                            + " startTime=" + r.receiverTime
-                            + " intent=" + r.intent
-                            + " numReceivers=" + numReceivers
-                            + " nextReceiver=" + r.nextReceiver
-                            + " state=" + r.state);
-                    broadcastTimeoutLocked(false); // forcibly finish this broadcast
-                    forceReceive = true;
-                    r.state = BroadcastRecord.IDLE;
-                }
-            }
-
-            if (r.state != BroadcastRecord.IDLE) {
-                if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
-                        "processNextBroadcast("
-                        + mQueueName + ") called when not idle (state="
-                        + r.state + ")");
-                return;
-            }
-
-            // Is the current broadcast is done for any reason?
-            if (r.receivers == null || r.nextReceiver >= numReceivers
-                    || r.resultAbort || forceReceive) {
-                // Send the final result if requested
-                if (r.resultTo != null) {
-                    boolean sendResult = true;
-
-                    // if this was part of a split/deferral complex, update the refcount and only
-                    // send the completion when we clear all of them
-                    if (r.splitToken != 0) {
-                        int newCount = mSplitRefcounts.get(r.splitToken) - 1;
-                        if (newCount == 0) {
-                            // done!  clear out this record's bookkeeping and deliver
-                            if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG_BROADCAST,
-                                        "Sending broadcast completion for split token "
-                                        + r.splitToken + " : " + r.intent.getAction());
-                            }
-                            mSplitRefcounts.delete(r.splitToken);
-                        } else {
-                            // still have some split broadcast records in flight; update refcount
-                            // and hold off on the callback
-                            if (DEBUG_BROADCAST_DEFERRAL) {
-                                Slog.i(TAG_BROADCAST,
-                                        "Result refcount now " + newCount + " for split token "
-                                        + r.splitToken + " : " + r.intent.getAction()
-                                        + " - not sending completion yet");
-                            }
-                            sendResult = false;
-                            mSplitRefcounts.put(r.splitToken, newCount);
-                        }
-                    }
-                    if (sendResult) {
-                        if (r.callerApp != null) {
-                            mService.mOomAdjuster.unfreezeTemporarily(
-                                    r.callerApp,
-                                    CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
-                        }
-                        try {
-                            if (DEBUG_BROADCAST) {
-                                Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
-                                        + r.intent.getAction() + " app=" + r.callerApp);
-                            }
-                            if (r.dispatchTime == 0) {
-                                // The dispatch time here could be 0, in case it's a parallel
-                                // broadcast but it has a result receiver. Set it to now.
-                                r.dispatchTime = now;
-                            }
-                            r.mIsReceiverAppRunning = true;
-                            performReceiveLocked(r, r.resultToApp, r.resultTo,
-                                    new Intent(r.intent), r.resultCode,
-                                    r.resultData, r.resultExtras, false, false, r.shareIdentity,
-                                    r.userId, r.callingUid, r.callingUid, r.callerPackage,
-                                    r.dispatchTime - r.enqueueTime,
-                                    now - r.dispatchTime, 0,
-                                    r.resultToApp != null
-                                            ? r.resultToApp.mState.getCurProcState()
-                                            : ActivityManager.PROCESS_STATE_UNKNOWN);
-                            logBootCompletedBroadcastCompletionLatencyIfPossible(r);
-                            // Set this to null so that the reference
-                            // (local and remote) isn't kept in the mBroadcastHistory.
-                            r.resultTo = null;
-                        } catch (RemoteException e) {
-                            r.resultTo = null;
-                            Slog.w(TAG, "Failure ["
-                                    + mQueueName + "] sending broadcast result of "
-                                    + r.intent, e);
-                        }
-                    }
-                }
-
-                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
-                cancelBroadcastTimeoutLocked();
-
-                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
-                        "Finished with ordered broadcast " + r);
-
-                // ... and on to the next...
-                addBroadcastToHistoryLocked(r);
-                if (r.intent.getComponent() == null && r.intent.getPackage() == null
-                        && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
-                    // This was an implicit broadcast... let's record it for posterity.
-                    mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
-                            r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
-                }
-                mDispatcher.retireBroadcastLocked(r);
-                r = null;
-                looped = true;
-                continue;
-            }
-
-            // Check whether the next receiver is under deferral policy, and handle that
-            // accordingly.  If the current broadcast was already part of deferred-delivery
-            // tracking, we know that it must now be deliverable as-is without re-deferral.
-            if (!r.deferred) {
-                final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
-                if (mDispatcher.isDeferringLocked(receiverUid)) {
-                    if (DEBUG_BROADCAST_DEFERRAL) {
-                        Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
-                                + " at " + r.nextReceiver + " is under deferral");
-                    }
-                    // If this is the only (remaining) receiver in the broadcast, "splitting"
-                    // doesn't make sense -- just defer it as-is and retire it as the
-                    // currently active outgoing broadcast.
-                    BroadcastRecord defer;
-                    if (r.nextReceiver + 1 == numReceivers) {
-                        if (DEBUG_BROADCAST_DEFERRAL) {
-                            Slog.i(TAG_BROADCAST, "Sole receiver of " + r
-                                    + " is under deferral; setting aside and proceeding");
-                        }
-                        defer = r;
-                        mDispatcher.retireBroadcastLocked(r);
-                    } else {
-                        // Nontrivial case; split out 'uid's receivers to a new broadcast record
-                        // and defer that, then loop and pick up continuing delivery of the current
-                        // record (now absent those receivers).
-
-                        // The split operation is guaranteed to match at least at 'nextReceiver'
-                        defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
-                        if (DEBUG_BROADCAST_DEFERRAL) {
-                            Slog.i(TAG_BROADCAST, "Post split:");
-                            Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
-                            for (int i = 0; i < r.receivers.size(); i++) {
-                                Slog.i(TAG_BROADCAST, "  " + r.receivers.get(i));
-                            }
-                            Slog.i(TAG_BROADCAST, "Split receivers:");
-                            for (int i = 0; i < defer.receivers.size(); i++) {
-                                Slog.i(TAG_BROADCAST, "  " + defer.receivers.get(i));
-                            }
-                        }
-                        // Track completion refcount as well if relevant
-                        if (r.resultTo != null) {
-                            int token = r.splitToken;
-                            if (token == 0) {
-                                // first split of this record; refcount for 'r' and 'deferred'
-                                r.splitToken = defer.splitToken = nextSplitTokenLocked();
-                                mSplitRefcounts.put(r.splitToken, 2);
-                                if (DEBUG_BROADCAST_DEFERRAL) {
-                                    Slog.i(TAG_BROADCAST,
-                                            "Broadcast needs split refcount; using new token "
-                                            + r.splitToken);
-                                }
-                            } else {
-                                // new split from an already-refcounted situation; increment count
-                                final int curCount = mSplitRefcounts.get(token);
-                                if (DEBUG_BROADCAST_DEFERRAL) {
-                                    if (curCount == 0) {
-                                        Slog.wtf(TAG_BROADCAST,
-                                                "Split refcount is zero with token for " + r);
-                                    }
-                                }
-                                mSplitRefcounts.put(token, curCount + 1);
-                                if (DEBUG_BROADCAST_DEFERRAL) {
-                                    Slog.i(TAG_BROADCAST, "New split count for token " + token
-                                            + " is " + (curCount + 1));
-                                }
-                            }
-                        }
-                    }
-                    mDispatcher.addDeferredBroadcast(receiverUid, defer);
-                    r = null;
-                    looped = true;
-                    continue;
-                }
-            }
-        } while (r == null);
-
-        // Get the next receiver...
-        int recIdx = r.nextReceiver++;
-
-        // Keep track of when this receiver started, and make sure there
-        // is a timeout message pending to kill it if need be.
-        r.receiverTime = SystemClock.uptimeMillis();
-        r.scheduledTime[recIdx] = r.receiverTime;
-        if (recIdx == 0) {
-            r.dispatchTime = r.receiverTime;
-            r.dispatchRealTime = SystemClock.elapsedRealtime();
-            r.dispatchClockTime = System.currentTimeMillis();
-
-            if (mLogLatencyMetrics) {
-                FrameworkStatsLog.write(
-                        FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
-                        r.dispatchClockTime - r.enqueueClockTime);
-            }
-
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-                Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
-                    System.identityHashCode(r));
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
-                    System.identityHashCode(r));
-            }
-            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
-                    + mQueueName + "] " + r);
-        }
-        if (! mPendingBroadcastTimeoutMessage) {
-            long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
-            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
-                    "Submitting BROADCAST_TIMEOUT_MSG ["
-                    + mQueueName + "] for " + r + " at " + timeoutTime);
-            setBroadcastTimeoutLocked(timeoutTime);
-        }
-
-        final BroadcastOptions brOptions = r.options;
-        final Object nextReceiver = r.receivers.get(recIdx);
-
-        if (nextReceiver instanceof BroadcastFilter) {
-            // Simple case: this is a registered receiver who gets
-            // a direct call.
-            BroadcastFilter filter = (BroadcastFilter)nextReceiver;
-            if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                    "Delivering ordered ["
-                    + mQueueName + "] to registered "
-                    + filter + ": " + r);
-            r.mIsReceiverAppRunning = true;
-            deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
-            if ((r.curReceiver == null && r.curFilter == null) || !r.ordered) {
-                // The receiver has already finished, so schedule to
-                // process the next one.
-                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
-                        + mQueueName + "]: ordered=" + r.ordered
-                        + " curFilter=" + r.curFilter
-                        + " curReceiver=" + r.curReceiver);
-                r.state = BroadcastRecord.IDLE;
-                scheduleBroadcastsLocked();
-            } else {
-                if (filter.receiverList != null) {
-                    maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
-                    // r is guaranteed ordered at this point, so we know finishReceiverLocked()
-                    // will get a callback and handle the activity start token lifecycle.
-                }
-            }
-            return;
-        }
-
-        // Hard case: need to instantiate the receiver, possibly
-        // starting its application process to host it.
-
-        final ResolveInfo info =
-            (ResolveInfo)nextReceiver;
-        final ComponentName component = new ComponentName(
-                info.activityInfo.applicationInfo.packageName,
-                info.activityInfo.name);
-        final int receiverUid = info.activityInfo.applicationInfo.uid;
-
-        final String targetProcess = info.activityInfo.processName;
-        final ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
-                info.activityInfo.applicationInfo.uid);
-
-        boolean skip = mSkipPolicy.shouldSkip(r, info);
-
-        // Filter packages in the intent extras, skipping delivery if none of the packages is
-        // visible to the receiver.
-        Bundle filteredExtras = null;
-        if (!skip && r.filterExtrasForReceiver != null) {
-            final Bundle extras = r.intent.getExtras();
-            if (extras != null) {
-                filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
-                if (filteredExtras == null) {
-                    if (DEBUG_BROADCAST) {
-                        Slog.v(TAG, "Skipping delivery to "
-                                + info.activityInfo.packageName + " / " + receiverUid
-                                + " : receiver is filtered by the package visibility");
-                    }
-                    skip = true;
-                }
-            }
-        }
-
-        if (skip) {
-            if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                    "Skipping delivery of ordered [" + mQueueName + "] "
-                    + r + " for reason described above");
-            r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
-            r.curFilter = null;
-            r.state = BroadcastRecord.IDLE;
-            r.manifestSkipCount++;
-            scheduleBroadcastsLocked();
-            return;
-        }
-        r.manifestCount++;
-
-        r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
-        r.state = BroadcastRecord.APP_RECEIVE;
-        r.curComponent = component;
-        r.curReceiver = info.activityInfo;
-        r.curFilteredExtras = filteredExtras;
-        if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
-            Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
-                    + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
-                    + receiverUid);
-        }
-        final boolean isActivityCapable =
-                (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
-        maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
-
-        // Report that a component is used for explicit broadcasts.
-        if (r.intent.getComponent() != null && r.curComponent != null
-                && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
-            mService.mUsageStatsService.reportEvent(
-                    r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
-        }
-
-        try {
-            mService.mPackageManagerInt.notifyComponentUsed(
-                    r.curComponent.getPackageName(), r.userId, r.callerPackage, r.toString());
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Failed trying to unstop package "
-                    + r.curComponent.getPackageName() + ": " + e);
-        }
-
-        // Is this receiver's application already running?
-        if (app != null && app.getThread() != null && !app.isKilled()) {
-            try {
-                app.addPackage(info.activityInfo.packageName,
-                        info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
-                maybeAddBackgroundStartPrivileges(app, r);
-                r.mIsReceiverAppRunning = true;
-                processCurBroadcastLocked(r, app);
-                return;
-            } catch (RemoteException e) {
-                final String msg = "Failed to schedule " + r.intent + " to " + info
-                        + " via " + app + ": " + e;
-                Slog.w(TAG, msg);
-                app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
-            } catch (RuntimeException e) {
-                Slog.wtf(TAG, "Failed sending broadcast to "
-                        + r.curComponent + " with " + r.intent, e);
-                // If some unexpected exception happened, just skip
-                // this broadcast.  At this point we are not in the call
-                // from a client, so throwing an exception out from here
-                // will crash the entire system instead of just whoever
-                // sent the broadcast.
-                logBroadcastReceiverDiscardLocked(r);
-                finishReceiverLocked(r, r.resultCode, r.resultData,
-                        r.resultExtras, r.resultAbort, false);
-                scheduleBroadcastsLocked();
-                // We need to reset the state if we failed to start the receiver.
-                r.state = BroadcastRecord.IDLE;
-                return;
-            }
-
-            // If a dead object exception was thrown -- fall through to
-            // restart the application.
-        }
-
-        // Registered whether we're bringing this package out of a stopped state
-        r.mWasReceiverAppStopped =
-                (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
-        // Not running -- get it started, to be executed when the app comes up.
-        if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
-                "Need to start app ["
-                + mQueueName + "] " + targetProcess + " for broadcast " + r);
-        r.curApp = mService.startProcessLocked(targetProcess,
-                info.activityInfo.applicationInfo, true,
-                r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
-                new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
-                        r.intent.getAction(), r.getHostingRecordTriggerType()),
-                isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
-                (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
-        r.curAppLastProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
-        if (r.curApp == null) {
-            // Ah, this recipient is unavailable.  Finish it if necessary,
-            // and mark the broadcast record as ready for the next.
-            Slog.w(TAG, "Unable to launch app "
-                    + info.activityInfo.applicationInfo.packageName + "/"
-                    + receiverUid + " for broadcast "
-                    + r.intent + ": process is bad");
-            logBroadcastReceiverDiscardLocked(r);
-            finishReceiverLocked(r, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, false);
-            scheduleBroadcastsLocked();
-            r.state = BroadcastRecord.IDLE;
-            return;
-        }
-
-        maybeAddBackgroundStartPrivileges(r.curApp, r);
-        mPendingBroadcast = r;
-        mPendingBroadcastRecvIndex = recIdx;
-    }
-
-    @Nullable
-    private String getTargetPackage(BroadcastRecord r) {
-        if (r.intent == null) {
-            return null;
-        }
-        if (r.intent.getPackage() != null) {
-            return r.intent.getPackage();
-        } else if (r.intent.getComponent() != null) {
-            return r.intent.getComponent().getPackageName();
-        }
-        return null;
-    }
-
-    static void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
-        // Only log after last receiver.
-        // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
-        // last BroadcastRecord of the split broadcast which has non-null resultTo.
-        final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
-        if (r.nextReceiver < numReceivers) {
-            return;
-        }
-        final String action = r.intent.getAction();
-        int event = 0;
-        if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
-            event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
-        } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-            event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
-        }
-        if (event != 0) {
-            final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
-            final int completeLatency = (int)
-                    (SystemClock.uptimeMillis() - r.enqueueTime);
-            final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
-            final int completeRealLatency = (int)
-                    (SystemClock.elapsedRealtime() - r.enqueueRealTime);
-            int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
-            // This method is called very infrequently, no performance issue we call
-            // LocalServices.getService() here.
-            final UserManagerInternal umInternal = LocalServices.getService(
-                    UserManagerInternal.class);
-            final UserInfo userInfo =
-                    (umInternal != null) ? umInternal.getUserInfo(r.userId) : null;
-            if (userInfo != null) {
-                userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType);
-            }
-            Slog.i(TAG_BROADCAST,
-                    "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
-                            + action
-                            + " dispatchLatency:" + dispatchLatency
-                            + " completeLatency:" + completeLatency
-                            + " dispatchRealLatency:" + dispatchRealLatency
-                            + " completeRealLatency:" + completeRealLatency
-                            + " receiversSize:" + numReceivers
-                            + " userId:" + r.userId
-                            + " userType:" + (userInfo != null? userInfo.userType : null));
-            FrameworkStatsLog.write(
-                    BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
-                    event,
-                    dispatchLatency,
-                    completeLatency,
-                    dispatchRealLatency,
-                    completeRealLatency,
-                    r.userId,
-                    userType);
-        }
-    }
-
-    private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
-        if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
-            return;
-        }
-        final String targetPackage = getTargetPackage(r);
-        // Ignore non-explicit broadcasts
-        if (targetPackage == null) {
-            return;
-        }
-        mService.mUsageStatsService.reportBroadcastDispatched(
-                r.callingUid, targetPackage, UserHandle.of(r.userId),
-                r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
-                mService.getUidStateLocked(targetUid));
-    }
-
-    private void maybeAddBackgroundStartPrivileges(ProcessRecord proc, BroadcastRecord r) {
-        if (r == null || proc == null || !r.mBackgroundStartPrivileges.allowsAny()) {
-            return;
-        }
-        String msgToken = (proc.toShortString() + r.toString()).intern();
-        // first, if there exists a past scheduled request to remove this token, drop
-        // that request - we don't want the token to be swept from under our feet...
-        mHandler.removeCallbacksAndMessages(msgToken);
-        // ...then add the token
-        proc.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);
-    }
-
-    final void setBroadcastTimeoutLocked(long timeoutTime) {
-        if (! mPendingBroadcastTimeoutMessage) {
-            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
-            mHandler.sendMessageAtTime(msg, timeoutTime);
-            mPendingBroadcastTimeoutMessage = true;
-        }
-    }
-
-    final void cancelBroadcastTimeoutLocked() {
-        if (mPendingBroadcastTimeoutMessage) {
-            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
-            mPendingBroadcastTimeoutMessage = false;
-        }
-    }
-
-    final void broadcastTimeoutLocked(boolean fromMsg) {
-        if (fromMsg) {
-            mPendingBroadcastTimeoutMessage = false;
-        }
-
-        if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
-            return;
-        }
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastTimeoutLocked()");
-        try {
-            long now = SystemClock.uptimeMillis();
-            BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
-            if (fromMsg) {
-                if (!mService.mProcessesReady) {
-                    // Only process broadcast timeouts if the system is ready; some early
-                    // broadcasts do heavy work setting up system facilities
-                    return;
-                }
-
-                // If the broadcast is generally exempt from timeout tracking, we're done
-                if (r.timeoutExempt) {
-                    if (DEBUG_BROADCAST) {
-                        Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
-                                + r.intent.getAction());
-                    }
-                    return;
-                }
-                long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
-                if (timeoutTime > now) {
-                    // We can observe premature timeouts because we do not cancel and reset the
-                    // broadcast timeout message after each receiver finishes.  Instead, we set up
-                    // an initial timeout then kick it down the road a little further as needed
-                    // when it expires.
-                    if (DEBUG_BROADCAST) {
-                        Slog.v(TAG_BROADCAST,
-                                "Premature timeout ["
-                                + mQueueName + "] @ " + now
-                                + ": resetting BROADCAST_TIMEOUT_MSG for "
-                                + timeoutTime);
-                    }
-                    setBroadcastTimeoutLocked(timeoutTime);
-                    return;
-                }
-            }
-
-            if (r.state == BroadcastRecord.WAITING_SERVICES) {
-                // In this case the broadcast had already finished, but we had decided to wait
-                // for started services to finish as well before going on.  So if we have actually
-                // waited long enough time timeout the broadcast, let's give up on the whole thing
-                // and just move on to the next.
-                Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
-                        ? r.curComponent.flattenToShortString() : "(null)"));
-                r.curComponent = null;
-                r.state = BroadcastRecord.IDLE;
-                processNextBroadcastLocked(false, false);
-                return;
-            }
-
-            // If the receiver app is being debugged we quietly ignore unresponsiveness, just
-            // tidying up and moving on to the next broadcast without crashing or ANRing this
-            // app just because it's stopped at a breakpoint.
-            final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
-
-            long timeoutDurationMs = now - r.receiverTime;
-            Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter
-                    + " curReceiver=" + r.curReceiver + ", started " + timeoutDurationMs
-                    + "ms ago");
-            r.receiverTime = now;
-            if (!debugging) {
-                r.anrCount++;
-            }
-
-            ProcessRecord app = null;
-            Object curReceiver;
-            if (r.nextReceiver > 0) {
-                curReceiver = r.receivers.get(r.nextReceiver - 1);
-                r.delivery[r.nextReceiver - 1] = BroadcastRecord.DELIVERY_TIMEOUT;
-            } else {
-                curReceiver = r.curReceiver;
-            }
-            Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
-            logBroadcastReceiverDiscardLocked(r);
-            TimeoutRecord timeoutRecord = TimeoutRecord.forBroadcastReceiver(r.intent,
-                    timeoutDurationMs);
-            if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter) curReceiver;
-                if (bf.receiverList.pid != 0
-                        && bf.receiverList.pid != ActivityManagerService.MY_PID) {
-                    timeoutRecord.mLatencyTracker.waitingOnPidLockStarted();
-                    synchronized (mService.mPidsSelfLocked) {
-                        timeoutRecord.mLatencyTracker.waitingOnPidLockEnded();
-                        app = mService.mPidsSelfLocked.get(
-                                bf.receiverList.pid);
-                    }
-                }
-            } else {
-                app = r.curApp;
-            }
-
-            if (mPendingBroadcast == r) {
-                mPendingBroadcast = null;
-            }
-
-            // Move on to the next receiver.
-            finishReceiverLocked(r, r.resultCode, r.resultData,
-                    r.resultExtras, r.resultAbort, false);
-            scheduleBroadcastsLocked();
-
-            // The ANR should only be triggered if we have a process record (app is non-null)
-            if (!debugging && app != null) {
-                mService.appNotResponding(app, timeoutRecord);
-            }
-
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-
-    }
-
-    private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
-        if (original.callingUid < 0) {
-            // This was from a registerReceiver() call; ignore it.
-            return;
-        }
-        original.finishTime = SystemClock.uptimeMillis();
-
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
-                System.identityHashCode(original));
-        }
-
-        mService.notifyBroadcastFinishedLocked(original);
-        mHistory.addBroadcastToHistoryLocked(original);
-    }
-
-    public boolean cleanupDisabledPackageReceiversLocked(
-            String packageName, Set<String> filterByClasses, int userId) {
-        boolean didSomething = false;
-        for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
-            didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
-                    packageName, filterByClasses, userId, true);
-        }
-
-        didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
-                filterByClasses, userId, true);
-
-        return didSomething;
-    }
-
-    final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
-        final int logIndex = r.nextReceiver - 1;
-        if (logIndex >= 0 && logIndex < r.receivers.size()) {
-            Object curReceiver = r.receivers.get(logIndex);
-            if (curReceiver instanceof BroadcastFilter) {
-                BroadcastFilter bf = (BroadcastFilter) curReceiver;
-                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
-                        bf.owningUserId, System.identityHashCode(r),
-                        r.intent.getAction(), logIndex, System.identityHashCode(bf));
-            } else {
-                ResolveInfo ri = (ResolveInfo) curReceiver;
-                EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                        UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
-                        System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
-            }
-        } else {
-            if (logIndex < 0) Slog.w(TAG,
-                    "Discarding broadcast before first receiver is invoked: " + r);
-            EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-                    -1, System.identityHashCode(r),
-                    r.intent.getAction(),
-                    r.nextReceiver,
-                    "NONE");
-        }
-    }
-
-    private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
-        return formatSimple("Broadcast %s from %s (%s) %s",
-                state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
-                record.callerPackage == null ? "" : record.callerPackage,
-                record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
-                record.intent == null ? "" : record.intent.getAction());
-    }
-
-    public boolean isIdleLocked() {
-        return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
-                && (mPendingBroadcast == null);
-    }
-
-    public boolean isBeyondBarrierLocked(long barrierTime) {
-        // If nothing active, we're beyond barrier
-        if (isIdleLocked()) return true;
-
-        // Check if parallel broadcasts are beyond barrier
-        for (int i = 0; i < mParallelBroadcasts.size(); i++) {
-            if (mParallelBroadcasts.get(i).enqueueTime <= barrierTime) {
-                return false;
-            }
-        }
-
-        // Check if pending broadcast is beyond barrier
-        final BroadcastRecord pending = getPendingBroadcastLocked();
-        if ((pending != null) && pending.enqueueTime <= barrierTime) {
-            return false;
-        }
-
-        return mDispatcher.isBeyondBarrier(barrierTime);
-    }
-
-    public boolean isDispatchedLocked(Intent intent) {
-        if (isIdleLocked()) return true;
-
-        for (int i = 0; i < mParallelBroadcasts.size(); i++) {
-            if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
-                return false;
-            }
-        }
-
-        final BroadcastRecord pending = getPendingBroadcastLocked();
-        if ((pending != null) && intent.filterEquals(pending.intent)) {
-            return false;
-        }
-
-        return mDispatcher.isDispatched(intent);
-    }
-
-    public void waitForIdle(PrintWriter pw) {
-        waitFor(() -> isIdleLocked(), pw, "idle");
-    }
-
-    public void waitForBarrier(PrintWriter pw) {
-        final long barrierTime = SystemClock.uptimeMillis();
-        waitFor(() -> isBeyondBarrierLocked(barrierTime), pw, "barrier");
-    }
-
-    public void waitForDispatched(Intent intent, PrintWriter pw) {
-        waitFor(() -> isDispatchedLocked(intent), pw, "dispatch");
-    }
-
-    private void waitFor(BooleanSupplier condition, PrintWriter pw, String conditionName) {
-        long lastPrint = 0;
-        while (true) {
-            synchronized (mService) {
-                if (condition.getAsBoolean()) {
-                    final String msg = "Queue [" + mQueueName + "] reached " + conditionName
-                            + " condition";
-                    Slog.v(TAG, msg);
-                    if (pw != null) {
-                        pw.println(msg);
-                        pw.flush();
-                    }
-                    return;
-                }
-            }
-
-            // Print at most every second
-            final long now = SystemClock.uptimeMillis();
-            if (now >= lastPrint + 1000) {
-                lastPrint = now;
-                final String msg = "Queue [" + mQueueName + "] waiting for " + conditionName
-                        + " condition; state is " + describeStateLocked();
-                Slog.v(TAG, msg);
-                if (pw != null) {
-                    pw.println(msg);
-                    pw.flush();
-                }
-            }
-
-            // Push through any deferrals to try meeting our condition
-            cancelDeferrals();
-            SystemClock.sleep(100);
-        }
-    }
-
-    // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
-    // be immediately deliverable.
-    public void cancelDeferrals() {
-        synchronized (mService) {
-            mDispatcher.cancelDeferralsLocked();
-            scheduleBroadcastsLocked();
-        }
-    }
-
-    public String describeStateLocked() {
-        return mParallelBroadcasts.size() + " parallel; "
-                + mDispatcher.describeStateLocked();
-    }
-
-    @NeverCompile
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        long token = proto.start(fieldId);
-        proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
-        int N;
-        N = mParallelBroadcasts.size();
-        for (int i = N - 1; i >= 0; i--) {
-            mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
-        }
-        mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
-        if (mPendingBroadcast != null) {
-            mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
-        }
-        mHistory.dumpDebug(proto);
-        proto.end(token);
-    }
-
-    @NeverCompile
-    public boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpConstants, boolean dumpHistory, boolean dumpAll,
-            String dumpPackage, boolean needSep) {
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-        if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
-                || mPendingBroadcast != null) {
-            boolean printed = false;
-            for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
-                BroadcastRecord br = mParallelBroadcasts.get(i);
-                if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
-                    continue;
-                }
-                if (!printed) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    needSep = true;
-                    printed = true;
-                    pw.println("  Active broadcasts [" + mQueueName + "]:");
-                }
-                pw.println("  Active Broadcast " + mQueueName + " #" + i + ":");
-                br.dump(pw, "    ", sdf);
-            }
-
-            mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
-
-            if (dumpPackage == null || (mPendingBroadcast != null
-                    && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
-                pw.println();
-                pw.println("  Pending broadcast [" + mQueueName + "]:");
-                if (mPendingBroadcast != null) {
-                    mPendingBroadcast.dump(pw, "    ", sdf);
-                } else {
-                    pw.println("    (null)");
-                }
-                needSep = true;
-            }
-        }
-        if (dumpConstants) {
-            mConstants.dump(new IndentingPrintWriter(pw));
-        }
-        if (dumpHistory) {
-            needSep = mHistory.dumpLocked(pw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
-        }
-        return needSep;
-    }
-}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 98263df..4422608 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -20,6 +20,9 @@
 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
 
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN;
@@ -59,6 +62,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.BundleMerger;
 import android.os.Handler;
@@ -85,9 +89,12 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
 import com.android.server.am.BroadcastProcessQueue.BroadcastConsumer;
 import com.android.server.am.BroadcastProcessQueue.BroadcastPredicate;
 import com.android.server.am.BroadcastRecord.DeliveryState;
+import com.android.server.pm.UserJourneyLogger;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.AnrTimer;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -2120,7 +2127,7 @@
         r.nextReceiver = r.receivers.size();
         mHistory.onBroadcastFinishedLocked(r);
 
-        BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+        logBootCompletedBroadcastCompletionLatencyIfPossible(r);
 
         if (r.intent.getComponent() == null && r.intent.getPackage() == null
                 && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
@@ -2216,6 +2223,59 @@
         return null;
     }
 
+    private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
+        // Only log after last receiver.
+        // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
+        // last BroadcastRecord of the split broadcast which has non-null resultTo.
+        final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+        if (r.nextReceiver < numReceivers) {
+            return;
+        }
+        final String action = r.intent.getAction();
+        int event = 0;
+        if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
+            event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
+        } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+        }
+        if (event != 0) {
+            final int dispatchLatency = (int) (r.dispatchTime - r.enqueueTime);
+            final int completeLatency = (int) (SystemClock.uptimeMillis() - r.enqueueTime);
+            final int dispatchRealLatency = (int) (r.dispatchRealTime - r.enqueueRealTime);
+            final int completeRealLatency = (int)
+                    (SystemClock.elapsedRealtime() - r.enqueueRealTime);
+            int userType =
+                    FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
+            // This method is called very infrequently, no performance issue we call
+            // LocalServices.getService() here.
+            final UserManagerInternal umInternal = LocalServices.getService(
+                    UserManagerInternal.class);
+            final UserInfo userInfo =
+                    (umInternal != null) ? umInternal.getUserInfo(r.userId) : null;
+            if (userInfo != null) {
+                userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType);
+            }
+            Slog.i(TAG, "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
+                    + action
+                    + " dispatchLatency:" + dispatchLatency
+                    + " completeLatency:" + completeLatency
+                    + " dispatchRealLatency:" + dispatchRealLatency
+                    + " completeRealLatency:" + completeRealLatency
+                    + " receiversSize:" + numReceivers
+                    + " userId:" + r.userId
+                    + " userType:" + (userInfo != null ? userInfo.userType : null));
+            FrameworkStatsLog.write(
+                    BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
+                    event,
+                    dispatchLatency,
+                    completeLatency,
+                    dispatchRealLatency,
+                    completeRealLatency,
+                    r.userId,
+                    userType);
+        }
+    }
+
     @Override
     @NeverCompile
     public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 4ef31bf..f2b9b25 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
 
 import android.annotation.Nullable;
 import android.app.IServiceConnection;
@@ -37,7 +38,7 @@
 /**
  * Description of a single binding to a service.
  */
-final class ConnectionRecord {
+final class ConnectionRecord implements OomAdjusterModernImpl.Connection{
     final AppBindRecord binding;    // The application/service binding.
     final ActivityServiceConnectionsHolder<ConnectionRecord> activity;  // If non-null, the owning activity.
     final IServiceConnection conn;  // The client connection.
@@ -127,6 +128,14 @@
         aliasComponent = _aliasComponent;
     }
 
+    @Override
+    public void computeHostOomAdjLSP(OomAdjuster oomAdjuster, ProcessRecord host,
+            ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
+            int oomAdjReason, int cachedAdj) {
+        oomAdjuster.computeServiceHostOomAdjLSP(this, host, client, now, topApp, doingAll, false,
+                false, oomAdjReason, UNKNOWN_ADJ, false, false);
+    }
+
     public long getFlags() {
         return flags;
     }
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index 825b938..3988277 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
 
 import android.annotation.UserIdInt;
 import android.os.Binder;
@@ -32,7 +33,8 @@
 /**
  * Represents a link between a content provider and client.
  */
-public final class ContentProviderConnection extends Binder {
+public final class ContentProviderConnection extends Binder implements
+        OomAdjusterModernImpl.Connection {
     public final ContentProviderRecord provider;
     public final ProcessRecord client;
     public final String clientPackage;
@@ -72,6 +74,14 @@
         createTime = SystemClock.elapsedRealtime();
     }
 
+    @Override
+    public void computeHostOomAdjLSP(OomAdjuster oomAdjuster, ProcessRecord host,
+            ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
+            int oomAdjReason, int cachedAdj) {
+        oomAdjuster.computeProviderHostOomAdjLSP(this, host, client, now, topApp, doingAll, false,
+                false, oomAdjReason, UNKNOWN_ADJ, false, false);
+    }
+
     public void startAssociationIfNeeded() {
         // If we don't already have an active association, create one...  but only if this
         // is an association between two different processes.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 31328ae..cd6964e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -587,7 +587,7 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
+    protected void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
         // Clear any pending ones because we are doing a full update now.
         mPendingProcessSet.clear();
@@ -913,7 +913,7 @@
     }
 
     @GuardedBy("mService")
-    private void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
+    protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
@@ -941,7 +941,7 @@
      * must have called {@link collectReachableProcessesLocked} on it.
      */
     @GuardedBy({"mService", "mProcLock"})
-    protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
+    private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
             ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
             boolean startProfiling) {
         final boolean fullUpdate = processes == null;
@@ -1775,12 +1775,11 @@
             state.setAdjSeq(mAdjSeq);
             state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND);
             state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
+            state.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
             state.setCurAdj(CACHED_APP_MAX_ADJ);
             state.setCurRawAdj(CACHED_APP_MAX_ADJ);
             state.setCompletedAdjSeq(state.getAdjSeq());
             state.setCurCapability(PROCESS_CAPABILITY_NONE);
-            onProcessStateChanged(app, prevProcState);
-            onProcessOomAdjChanged(app, prevAppAdj);
             return false;
         }
 
@@ -1843,8 +1842,6 @@
             state.setCurRawProcState(state.getCurProcState());
             state.setCurAdj(state.getMaxAdj());
             state.setCompletedAdjSeq(state.getAdjSeq());
-            onProcessStateChanged(app, prevProcState);
-            onProcessOomAdjChanged(app, prevAppAdj);
             // if curAdj is less than prevAppAdj, then this process was promoted
             return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
         }
@@ -2561,7 +2558,7 @@
     }
 
     @GuardedBy({"mService", "mProcLock"})
-    protected boolean computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
+    public boolean computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
             ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
             boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
             boolean couldRecurse, boolean dryRun) {
@@ -2991,7 +2988,11 @@
         return updated;
     }
 
-    protected boolean computeProviderHostOomAdjLSP(ContentProviderConnection conn,
+    /**
+     * Computes the impact on {@code app} the provider connections from {@code client} has.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    public boolean computeProviderHostOomAdjLSP(ContentProviderConnection conn,
             ProcessRecord app, ProcessRecord client, long now, ProcessRecord topApp,
             boolean doingAll, boolean cycleReEval, boolean computeClients, int oomAdjReason,
             int cachedAdj, boolean couldRecurse, boolean dryRun) {
@@ -3572,7 +3573,7 @@
         int initialCapability =  PROCESS_CAPABILITY_NONE;
         boolean initialCached = true;
         final ProcessStateRecord state = app.mState;
-        final int prevProcState = state.getCurRawProcState();
+        final int prevProcState = state.getCurProcState();
         final int prevAdj = state.getCurRawAdj();
         // If the process has been marked as foreground, it is starting as the top app (with
         // Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 1bf779a..dd75bc0 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -38,7 +38,6 @@
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
@@ -67,8 +66,10 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal.OomAdjReason;
 import android.content.pm.ServiceInfo;
+import android.os.IBinder;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -80,6 +81,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -275,6 +277,7 @@
                 mProcessRecordNodes[i] = new LinkedProcessRecordList(type);
             }
             mLastNode = new ProcessRecordNode[size];
+            resetLastNodes();
         }
 
         int size() {
@@ -285,7 +288,7 @@
         void reset() {
             for (int i = 0; i < mProcessRecordNodes.length; i++) {
                 mProcessRecordNodes[i].reset();
-                mLastNode[i] = null;
+                setLastNodeToHead(i);
             }
         }
 
@@ -509,26 +512,118 @@
     }
 
     /**
-     * A helper consumer for collecting processes that have not been reached yet. To avoid object
-     * allocations every OomAdjuster update, the results will be stored in
-     * {@link UnreachedProcessCollector#processList}. The process list reader is responsible
-     * for setting it before usage, as well as, clearing the reachable state of each process in the
-     * list.
+     * A {@link Connection} represents any connection between two processes that can cause a
+     * change in importance in the host process based on the client process and connection state.
      */
-    private static class UnreachedProcessCollector implements Consumer<ProcessRecord> {
-        public ArrayList<ProcessRecord> processList = null;
+    public interface Connection {
+        /**
+         * Compute the impact this connection has on the host's importance values.
+         */
+        void computeHostOomAdjLSP(OomAdjuster oomAdjuster, ProcessRecord host, ProcessRecord client,
+                long now, ProcessRecord topApp, boolean doingAll, int oomAdjReason, int cachedAdj);
+    }
+
+    /**
+     * A helper consumer for marking and collecting reachable processes.
+     */
+    private static class ReachableCollectingConsumer implements
+            BiConsumer<Connection, ProcessRecord> {
+        ArrayList<ProcessRecord> mReachables = null;
+
+        public void init(ArrayList<ProcessRecord> reachables) {
+            mReachables = reachables;
+        }
+
         @Override
-        public void accept(ProcessRecord process) {
-            if (process.mState.isReachable()) {
+        public void accept(Connection unused, ProcessRecord host) {
+            if (host.mState.isReachable()) {
                 return;
             }
-            process.mState.setReachable(true);
-            processList.add(process);
+            host.mState.setReachable(true);
+            mReachables.add(host);
         }
     }
 
-    private final UnreachedProcessCollector mUnreachedProcessCollector =
-            new UnreachedProcessCollector();
+    private final ReachableCollectingConsumer mReachableCollectingConsumer =
+            new ReachableCollectingConsumer();
+
+    /**
+     * A helper consumer for computing the importance of a connection from a client.
+     * Connections for clients marked reachable will be ignored.
+     */
+    private class ComputeConnectionIgnoringReachableClientsConsumer implements
+            BiConsumer<Connection, ProcessRecord> {
+        public OomAdjusterArgs args = null;
+
+        @Override
+        public void accept(Connection conn, ProcessRecord client) {
+            final ProcessRecord host = args.mApp;
+            final ProcessRecord topApp = args.mTopApp;
+            final long now = args.mNow;
+            final @OomAdjReason int oomAdjReason = args.mOomAdjReason;
+
+            if (client.mState.isReachable()) return;
+
+            conn.computeHostOomAdjLSP(OomAdjusterModernImpl.this, host, client, now, topApp, false,
+                    oomAdjReason, UNKNOWN_ADJ);
+        }
+    }
+
+    private final ComputeConnectionIgnoringReachableClientsConsumer
+            mComputeConnectionIgnoringReachableClientsConsumer =
+            new ComputeConnectionIgnoringReachableClientsConsumer();
+
+    /**
+     * A helper consumer for computing host process importance from a connection from a client app.
+     */
+    private class ComputeHostConsumer implements BiConsumer<Connection, ProcessRecord> {
+        public OomAdjusterArgs args = null;
+
+        @Override
+        public void accept(Connection conn, ProcessRecord host) {
+            final ProcessRecord client = args.mApp;
+            final int cachedAdj = args.mCachedAdj;
+            final ProcessRecord topApp = args.mTopApp;
+            final long now = args.mNow;
+            final @OomAdjReason int oomAdjReason = args.mOomAdjReason;
+            final boolean fullUpdate = args.mFullUpdate;
+
+            final int prevProcState = host.mState.getCurProcState();
+            final int prevAdj = host.mState.getCurRawAdj();
+
+            conn.computeHostOomAdjLSP(OomAdjusterModernImpl.this, host, client, now, topApp,
+                    fullUpdate, oomAdjReason, cachedAdj);
+
+            updateProcStateSlotIfNecessary(host, prevProcState);
+            updateAdjSlotIfNecessary(host, prevAdj);
+        }
+    }
+    private final ComputeHostConsumer mComputeHostConsumer = new ComputeHostConsumer();
+
+    /**
+     * A helper consumer for computing all connections from an app.
+     */
+    private class ComputeConnectionsConsumer implements Consumer<OomAdjusterArgs> {
+        @Override
+        public void accept(OomAdjusterArgs args) {
+            final ProcessRecord app = args.mApp;
+            final ActiveUids uids = args.mUids;
+
+            // This process was updated in some way, mark that it was last calculated this sequence.
+            app.mState.setCompletedAdjSeq(mAdjSeq);
+            if (uids != null) {
+                final UidRecord uidRec = app.getUidRecord();
+
+                if (uidRec != null) {
+                    uids.put(uidRec.getUid(), uidRec);
+                }
+            }
+            mComputeHostConsumer.args = args;
+            forEachConnectionLSP(app, mComputeHostConsumer);
+        }
+    }
+    private final ComputeConnectionsConsumer mComputeConnectionsConsumer =
+            new ComputeConnectionsConsumer();
 
     OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
             ActiveUids activeUids) {
@@ -617,6 +712,12 @@
         }
     }
 
+    private void updateAdjSlot(ProcessRecord app, int prevRawAdj) {
+        final int slot = adjToSlot(app.mState.getCurRawAdj());
+        final int prevSlot = adjToSlot(prevRawAdj);
+        mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+    }
+
     private void updateProcStateSlotIfNecessary(ProcessRecord app, int prevProcState) {
         if (app.mState.getCurProcState() != prevProcState) {
             final int slot = processStateToSlot(app.mState.getCurProcState());
@@ -627,359 +728,247 @@
         }
     }
 
+    private void updateProcStateSlot(ProcessRecord app, int prevProcState) {
+        final int slot = processStateToSlot(app.mState.getCurProcState());
+        final int prevSlot = processStateToSlot(prevProcState);
+        mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+    }
+
     @Override
-    protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
+    protected void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
+        // Clear any pending ones because we are doing a full update now.
+        mPendingProcessSet.clear();
+        mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         mService.mOomAdjProfiler.oomAdjStarted();
-        mAdjSeq++;
 
-        final ProcessStateRecord state = app.mState;
-        final int oldAdj = state.getCurRawAdj();
-        final int cachedAdj = oldAdj >= CACHED_APP_MIN_ADJ
-                ? oldAdj : UNKNOWN_ADJ;
-
-        final ActiveUids uids = mTmpUidRecords;
-        final ArraySet<ProcessRecord> targetProcesses = mTmpProcessSet;
-        final ArrayList<ProcessRecord> reachableProcesses = mTmpProcessList;
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
-
-        uids.clear();
-        targetProcesses.clear();
-        targetProcesses.add(app);
-        reachableProcesses.clear();
-
-        // Find out all reachable processes from this app.
-        collectReachableProcessesLocked(targetProcesses, reachableProcesses, uids);
-
-        // Copy all of the reachable processes into the target process set.
-        targetProcesses.addAll(reachableProcesses);
-        reachableProcesses.clear();
-
-        final boolean result = performNewUpdateOomAdjLSP(oomAdjReason,
-                topApp, targetProcesses, uids, false, now, cachedAdj);
-
-        reachableProcesses.addAll(targetProcesses);
-        assignCachedAdjIfNecessary(reachableProcesses);
-        for (int  i = uids.size() - 1; i >= 0; i--) {
-            final UidRecord uidRec = uids.valueAt(i);
-            uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
-        }
-        updateUidsLSP(uids, nowElapsed);
-        for (int i = 0, size = targetProcesses.size(); i < size; i++) {
-            applyOomAdjLSP(targetProcesses.valueAt(i), false, now, nowElapsed, oomAdjReason);
-        }
-        targetProcesses.clear();
-        reachableProcesses.clear();
+        fullUpdateLSP(oomAdjReason);
 
         mService.mOomAdjProfiler.oomAdjEnded();
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        return result;
     }
 
     @GuardedBy({"mService", "mProcLock"})
     @Override
-    protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
-            ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
-            boolean startProfiling) {
-        final boolean fullUpdate = processes == null;
-        final ArrayList<ProcessRecord> activeProcesses = fullUpdate
-                ? mProcessList.getLruProcessesLOSP() : processes;
-        ActiveUids activeUids = uids;
-        if (activeUids == null) {
-            final int numUids = mActiveUids.size();
-            activeUids = mTmpUidRecords;
-            activeUids.clear();
-            for (int i = 0; i < numUids; i++) {
-                UidRecord uidRec = mActiveUids.valueAt(i);
-                activeUids.put(uidRec.getUid(), uidRec);
-            }
-        }
-
-        if (startProfiling) {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
-            mService.mOomAdjProfiler.oomAdjStarted();
-        }
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
-        final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
-        final int numProc = activeProcesses.size();
-
-        mAdjSeq++;
-        if (fullUpdate) {
-            mNewNumServiceProcs = 0;
-            mNewNumAServiceProcs = 0;
-        }
-
-        final ArraySet<ProcessRecord> targetProcesses = mTmpProcessSet;
-        targetProcesses.clear();
-        if (!fullUpdate) {
-            targetProcesses.addAll(activeProcesses);
-        }
-
-        performNewUpdateOomAdjLSP(oomAdjReason, topApp, targetProcesses, activeUids,
-                fullUpdate, now, UNKNOWN_ADJ);
-
-        // TODO: b/319163103 - optimize cache adj assignment to not require the whole lru list.
-        assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
-        postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
-        targetProcesses.clear();
-
-        if (startProfiling) {
-            mService.mOomAdjProfiler.oomAdjEnded();
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-        return;
-    }
-
-    /**
-     * Perform the oom adj update on the given {@code targetProcesses}.
-     *
-     * <p>Note: The expectation to the given {@code targetProcesses} is, the caller
-     * must have called {@link collectReachableProcessesLocked} on it.
-     */
-    private boolean performNewUpdateOomAdjLSP(@OomAdjReason int oomAdjReason,
-            ProcessRecord topApp,  ArraySet<ProcessRecord> targetProcesses, ActiveUids uids,
-            boolean fullUpdate, long now, int cachedAdj) {
-
-        final ArrayList<ProcessRecord> clientProcesses = mTmpProcessList2;
-        clientProcesses.clear();
-
-        // We'll need to collect the upstream processes of the target apps here, because those
-        // processes would potentially impact the procstate/adj via bindings.
-        if (!fullUpdate) {
-            collectExcludedClientProcessesLocked(targetProcesses, clientProcesses);
-
-            for (int i = 0, size = targetProcesses.size(); i < size; i++) {
-                final ProcessRecord app = targetProcesses.valueAt(i);
-                app.mState.resetCachedInfo();
-                final UidRecord uidRec = app.getUidRecord();
-                if (uidRec != null) {
-                    if (DEBUG_UID_OBSERVERS) {
-                        Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
-                    }
-                    uidRec.reset();
-                }
-            }
-        } else {
-            final ArrayList<ProcessRecord> lru = mProcessList.getLruProcessesLOSP();
-            for (int i = 0, size = lru.size(); i < size; i++) {
-                final ProcessRecord app = lru.get(i);
-                app.mState.resetCachedInfo();
-                final UidRecord uidRec = app.getUidRecord();
-                if (uidRec != null) {
-                    if (DEBUG_UID_OBSERVERS) {
-                        Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
-                    }
-                    uidRec.reset();
-                }
-            }
-        }
-
-        updateNewOomAdjInnerLSP(oomAdjReason, topApp, targetProcesses, clientProcesses, uids,
-                cachedAdj, now, fullUpdate);
-
-        clientProcesses.clear();
-
+    protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
+        mPendingProcessSet.add(app);
+        performUpdateOomAdjPendingTargetsLocked(oomAdjReason);
         return true;
     }
 
-    /**
-     * Collect the client processes from the given {@code apps}, the result will be returned in the
-     * given {@code clientProcesses}, which will <em>NOT</em> include the processes from the given
-     * {@code apps}.
-     */
     @GuardedBy("mService")
-    private void collectExcludedClientProcessesLocked(ArraySet<ProcessRecord> apps,
-            ArrayList<ProcessRecord> clientProcesses) {
-        // Mark all of the provided apps as reachable to avoid including them in the client list.
-        final int appsSize = apps.size();
-        for (int i = 0; i < appsSize; i++) {
-            final ProcessRecord app = apps.valueAt(i);
-            app.mState.setReachable(true);
-        }
+    @Override
+    protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
+        mService.mOomAdjProfiler.oomAdjStarted();
 
-        clientProcesses.clear();
-        mUnreachedProcessCollector.processList = clientProcesses;
-        for (int i = 0; i < appsSize; i++) {
-            final ProcessRecord app = apps.valueAt(i);
-            app.forEachClient(mUnreachedProcessCollector);
+        synchronized (mProcLock) {
+            partialUpdateLSP(oomAdjReason, mPendingProcessSet);
         }
-        mUnreachedProcessCollector.processList = null;
+        mPendingProcessSet.clear();
 
-        // Reset the temporary bits.
-        for (int i = clientProcesses.size() - 1; i >= 0; i--) {
-            clientProcesses.get(i).mState.setReachable(false);
-        }
-        for (int i = 0, size = apps.size(); i < size; i++) {
-            final ProcessRecord app = apps.valueAt(i);
-            app.mState.setReachable(false);
-        }
+        mService.mOomAdjProfiler.oomAdjEnded();
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    /**
+     * Perform a full update on the entire process list.
+     */
     @GuardedBy({"mService", "mProcLock"})
-    private void updateNewOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
-            ArraySet<ProcessRecord> targetProcesses, ArrayList<ProcessRecord> clientProcesses,
-            ActiveUids uids, int cachedAdj, long now, boolean fullUpdate) {
-        mTmpOomAdjusterArgs.update(topApp, now, cachedAdj, oomAdjReason, uids, fullUpdate);
-
-        mProcessRecordProcStateNodes.resetLastNodes();
-        mProcessRecordAdjNodes.resetLastNodes();
-
-        final int procStateTarget = mProcessRecordProcStateNodes.size() - 1;
-        final int adjTarget = mProcessRecordAdjNodes.size() - 1;
+    private void fullUpdateLSP(@OomAdjReason int oomAdjReason) {
+        final ProcessRecord topApp = mService.getTopApp();
+        final long now = SystemClock.uptimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
 
         mAdjSeq++;
-        // All apps to be updated will be moved to the lowest slot.
-        if (fullUpdate) {
-            // Move all the process record node to the lowest slot, we'll do recomputation on all of
-            // them. Use the processes from the lru list, because the scanning order matters here.
-            final ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
-            for (int i = procStateTarget; i >= 0; i--) {
-                mProcessRecordProcStateNodes.reset(i);
-                // Force the last node to the head since we'll recompute all of them.
-                mProcessRecordProcStateNodes.setLastNodeToHead(i);
+
+        mNewNumServiceProcs = 0;
+        mNewNumAServiceProcs = 0;
+
+        // Clear the priority queues.
+        mProcessRecordProcStateNodes.reset();
+        mProcessRecordAdjNodes.reset();
+
+        final ArrayList<ProcessRecord> lru = mProcessList.getLruProcessesLOSP();
+        for (int i = lru.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = lru.get(i);
+            final int prevProcState = app.mState.getCurProcState();
+            final int prevAdj = app.mState.getCurRawAdj();
+            app.mState.resetCachedInfo();
+            final UidRecord uidRec = app.getUidRecord();
+            if (uidRec != null) {
+                if (DEBUG_UID_OBSERVERS) {
+                    Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+                }
+                uidRec.reset();
             }
-            // enqueue the targets in the reverse order of the lru list.
-            for (int i = lruList.size() - 1; i >= 0; i--) {
-                mProcessRecordProcStateNodes.append(lruList.get(i), procStateTarget);
-            }
-            // Do the same to the adj nodes.
-            for (int i = adjTarget; i >= 0; i--) {
-                mProcessRecordAdjNodes.reset(i);
-                // Force the last node to the head since we'll recompute all of them.
-                mProcessRecordAdjNodes.setLastNodeToHead(i);
-            }
-            for (int i = lruList.size() - 1; i >= 0; i--) {
-                mProcessRecordAdjNodes.append(lruList.get(i), adjTarget);
-            }
-        } else {
-            // Move the target processes to the lowest slot.
-            for (int i = 0, size = targetProcesses.size(); i < size; i++) {
-                final ProcessRecord app = targetProcesses.valueAt(i);
-                final int procStateSlot = processStateToSlot(app.mState.getCurProcState());
-                final int adjSlot = adjToSlot(app.mState.getCurRawAdj());
-                mProcessRecordProcStateNodes.moveAppTo(app, procStateSlot, procStateTarget);
-                mProcessRecordAdjNodes.moveAppTo(app, adjSlot, adjTarget);
-            }
-            // Move the "lastNode" to head to make sure we scan all nodes in this slot.
-            mProcessRecordProcStateNodes.setLastNodeToHead(procStateTarget);
-            mProcessRecordAdjNodes.setLastNodeToHead(adjTarget);
+
+            // Compute initial values, the procState and adj priority queues will be populated here.
+            computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now, false, false, oomAdjReason,
+                    false);
+            updateProcStateSlot(app, prevProcState);
+            updateAdjSlot(app, prevAdj);
         }
 
-        // All apps to be updated have been moved to the lowest slot.
-        // Do an initial pass of the computation.
-        mProcessRecordProcStateNodes.forEachNewNode(mProcessRecordProcStateNodes.size() - 1,
-                this::computeInitialOomAdjLSP);
-
-        if (!fullUpdate) {
-            // We didn't update the client processes with the computeInitialOomAdjLSP
-            // because they don't need to do so. But they'll be playing vital roles in
-            // computing the bindings. So include them into the scan list below.
-            for (int i = 0, size = clientProcesses.size(); i < size; i++) {
-                mProcessRecordProcStateNodes.moveAppToTail(clientProcesses.get(i));
-            }
-            // We don't update the adj list since we're resetting it below.
-        }
-
-        // Now nodes are set into their slots, without factoring in the bindings.
-        // The nodes between the `lastNode` pointer and the TAIL should be the new nodes.
-        //
-        // The whole rationale here is that, the bindings from client to host app, won't elevate
-        // the host app's procstate/adj higher than the client app's state (BIND_ABOVE_CLIENT
-        // is a special case here, but client app's raw adj is still no less than the host app's).
-        // Therefore, starting from the top to the bottom, for each slot, scan all of the new nodes,
-        // check its bindings, elevate its host app's slot if necessary.
-        //
-        // We'd have to do this in two passes: 1) scan procstate node list; 2) scan adj node list.
-        // Because the procstate and adj are not always in sync - there are cases where
-        // the processes with lower proc state could be getting a higher oom adj score.
-        // And because of this, the procstate and adj node lists are basically two priority heaps.
-        //
-        // As the 2nd pass with the adj node lists potentially includes a significant amount of
-        // duplicated scans as the 1st pass has done, we'll reset the last node pointers for
-        // the adj node list before the 1st pass; so during the 1st pass, if any app's adj slot
-        // gets bumped, we'll only scan those in 2nd pass.
-
+        // Set adj last nodes now, this way a process will only be reevaluated during the adj node
+        // iteration if they adj score changed during the procState node iteration.
         mProcessRecordAdjNodes.resetLastNodes();
+        mTmpOomAdjusterArgs.update(topApp, now, UNKNOWN_ADJ, oomAdjReason, null, true);
+        computeConnectionsLSP();
 
+        assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+        postUpdateOomAdjInnerLSP(oomAdjReason, mActiveUids, now, nowElapsed, oldTime);
+    }
+
+    /**
+     * Traverse the process graph and update processes based on changes in connection importances.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private void computeConnectionsLSP() {
         // 1st pass, scan each slot in the procstate node list.
         for (int i = 0, end = mProcessRecordProcStateNodes.size() - 1; i < end; i++) {
-            mProcessRecordProcStateNodes.forEachNewNode(i, this::computeHostOomAdjLSP);
+            mProcessRecordProcStateNodes.forEachNewNode(i, mComputeConnectionsConsumer);
         }
 
         // 2nd pass, scan each slot in the adj node list.
         for (int i = 0, end = mProcessRecordAdjNodes.size() - 1; i < end; i++) {
-            mProcessRecordAdjNodes.forEachNewNode(i, this::computeHostOomAdjLSP);
+            mProcessRecordAdjNodes.forEachNewNode(i, mComputeConnectionsConsumer);
         }
     }
 
-    @GuardedBy({"mService", "mProcLock"})
-    private void computeInitialOomAdjLSP(OomAdjusterArgs args) {
-        final ProcessRecord app = args.mApp;
-        final int cachedAdj = args.mCachedAdj;
-        final ProcessRecord topApp = args.mTopApp;
-        final long now = args.mNow;
-        final int oomAdjReason = args.mOomAdjReason;
-        final ActiveUids uids = args.mUids;
-        final boolean fullUpdate = args.mFullUpdate;
-
-        if (DEBUG_OOM_ADJ) {
-            Slog.i(TAG, "OOM ADJ initial args app=" + app
-                    + " cachedAdj=" + cachedAdj
-                    + " topApp=" + topApp
-                    + " now=" + now
-                    + " oomAdjReason=" + oomAdjReasonToString(oomAdjReason)
-                    + " fullUpdate=" + fullUpdate);
-        }
-
-        if (uids != null) {
-            final UidRecord uidRec = app.getUidRecord();
-
-            if (uidRec != null) {
-                uids.put(uidRec.getUid(), uidRec);
-            }
-        }
-
-        computeOomAdjLSP(app, cachedAdj, topApp, fullUpdate, now, false, false, oomAdjReason,
-                false);
-    }
-
     /**
-     * @return The proposed change to the schedGroup.
+     * Perform a partial update on the target processes and their reachable processes.
      */
     @GuardedBy({"mService", "mProcLock"})
-    @Override
-    protected int setIntermediateAdjLSP(ProcessRecord app, int adj, int prevRawAppAdj,
-            int schedGroup) {
-        schedGroup = super.setIntermediateAdjLSP(app, adj, prevRawAppAdj, schedGroup);
+    private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) {
+        final ProcessRecord topApp = mService.getTopApp();
+        final long now = SystemClock.uptimeMillis();
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
 
-        updateAdjSlotIfNecessary(app, prevRawAppAdj);
+        ActiveUids activeUids = mTmpUidRecords;
+        activeUids.clear();
+        mTmpOomAdjusterArgs.update(topApp, now, UNKNOWN_ADJ, oomAdjReason, activeUids, false);
 
-        return schedGroup;
+        mAdjSeq++;
+
+        final ArrayList<ProcessRecord> reachables = mTmpProcessList;
+        reachables.clear();
+
+        for (int i = 0, size = targets.size(); i < size; i++) {
+            final ProcessRecord target = targets.valueAtUnchecked(i);
+            target.mState.resetCachedInfo();
+            target.mState.setReachable(true);
+            reachables.add(target);
+        }
+
+        // Collect all processes that are reachable.
+        // Any process not found in this step will not change in importance during this update.
+        collectAndMarkReachableProcessesLSP(reachables);
+
+        // Initialize the reachable processes based on their own values plus any
+        // connections from processes not found in the previous step. Since those non-reachable
+        // processes cannot change as a part of this update, their current values can be used
+        // right now.
+        mProcessRecordProcStateNodes.resetLastNodes();
+        initReachableStatesLSP(reachables, mTmpOomAdjusterArgs);
+
+        // Set adj last nodes now, this way a process will only be reevaluated during the adj node
+        // iteration if they adj score changed during the procState node iteration.
+        mProcessRecordAdjNodes.resetLastNodes();
+        // Now traverse and compute the connections of processes with changed importance.
+        computeConnectionsLSP();
+
+        boolean unassignedAdj = false;
+        for (int i = 0, size = reachables.size(); i < size; i++) {
+            final ProcessStateRecord state = reachables.get(i).mState;
+            state.setReachable(false);
+            state.setCompletedAdjSeq(mAdjSeq);
+            if (state.getCurAdj() >= UNKNOWN_ADJ) {
+                unassignedAdj = true;
+            }
+        }
+
+        // If all processes have an assigned adj, no need to calculate and assign cached adjs.
+        if (unassignedAdj) {
+            // TODO: b/319163103 - optimize cache adj assignment to not require the whole lru list.
+            assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
+        }
+
+        // Repopulate any uid record that may have changed.
+        for (int i = 0, size = activeUids.size(); i < size; i++) {
+            final UidRecord ur = activeUids.valueAt(i);
+            ur.reset();
+            for (int j = ur.getNumOfProcs() - 1; j >= 0; j--) {
+                final ProcessRecord proc = ur.getProcessRecordByIndex(j);
+                updateAppUidRecIfNecessaryLSP(proc);
+            }
+        }
+
+        postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
     }
 
+    /**
+     * Mark all processes reachable from the {@code reachables} processes and add them to the
+     * provided {@code reachables} list (targets excluded).
+     *
+     * Returns true if a cycle exists within the reachable process graph.
+     */
     @GuardedBy({"mService", "mProcLock"})
-    @Override
-    protected void setIntermediateProcStateLSP(ProcessRecord app, int procState,
-            int prevProcState) {
-        super.setIntermediateProcStateLSP(app, procState, prevProcState);
-
-        updateProcStateSlotIfNecessary(app, prevProcState);
+    private void collectAndMarkReachableProcessesLSP(ArrayList<ProcessRecord> reachables) {
+        mReachableCollectingConsumer.init(reachables);
+        for (int i = 0; i < reachables.size(); i++) {
+            ProcessRecord pr = reachables.get(i);
+            forEachConnectionLSP(pr, mReachableCollectingConsumer);
+        }
     }
 
+    /**
+     * Calculate initial importance states for {@code reachables} and update their slot position
+     * if necessary.
+     */
+    private void initReachableStatesLSP(ArrayList<ProcessRecord> reachables, OomAdjusterArgs args) {
+        for (int i = 0, size = reachables.size(); i < size; i++) {
+            final ProcessRecord reachable = reachables.get(i);
+            final int prevProcState = reachable.mState.getCurProcState();
+            final int prevAdj = reachable.mState.getCurRawAdj();
+
+            args.mApp = reachable;
+            computeOomAdjIgnoringReachablesLSP(args);
+
+            updateProcStateSlot(reachable, prevProcState);
+            updateAdjSlot(reachable, prevAdj);
+        }
+    }
+
+    /**
+     * Calculate initial importance states for {@code app}.
+     * Processes not marked reachable cannot change as a part of this update, so connections from
+     * those process can be calculated now.
+     */
     @GuardedBy({"mService", "mProcLock"})
-    private void computeHostOomAdjLSP(OomAdjusterArgs args) {
+    private void computeOomAdjIgnoringReachablesLSP(OomAdjusterArgs args) {
         final ProcessRecord app = args.mApp;
-        final int cachedAdj = args.mCachedAdj;
         final ProcessRecord topApp = args.mTopApp;
         final long now = args.mNow;
         final @OomAdjReason int oomAdjReason = args.mOomAdjReason;
-        final boolean fullUpdate = args.mFullUpdate;
-        final ActiveUids uids = args.mUids;
 
+        computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, false, now, false, false, oomAdjReason, false);
+
+        mComputeConnectionIgnoringReachableClientsConsumer.args = args;
+        forEachClientConnectionLSP(app, mComputeConnectionIgnoringReachableClientsConsumer);
+    }
+
+    /**
+     * Stream the connections with {@code app} as a client to
+     * {@code connectionConsumer}.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private static void forEachConnectionLSP(ProcessRecord app,
+            BiConsumer<Connection, ProcessRecord> connectionConsumer) {
         final ProcessServiceRecord psr = app.mServices;
         for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
             ConnectionRecord cr = psr.getConnectionAt(i);
@@ -987,16 +976,14 @@
                     ? cr.binding.service.isolationHostProc : cr.binding.service.app;
             if (service == null || service == app
                     || (service.mState.getMaxAdj() >= SYSTEM_ADJ
-                            && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+                    && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
                     || (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ
-                            && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
-                            && service.mState.getCurProcState() <= PROCESS_STATE_TOP)
+                    && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+                    && service.mState.getCurProcState() <= PROCESS_STATE_TOP)
                     || (service.isSdkSandbox && cr.binding.attributedClient != null)) {
                 continue;
             }
-
-            computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false, false);
+            connectionConsumer.accept(cr, service);
         }
 
         for (int i = psr.numberOfSdkSandboxConnections() - 1; i >= 0; i--) {
@@ -1004,15 +991,13 @@
             final ProcessRecord service = cr.binding.service.app;
             if (service == null || service == app
                     || (service.mState.getMaxAdj() >= SYSTEM_ADJ
-                            && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+                    && service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
                     || (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ
-                            && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
-                            && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+                    && service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+                    && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
                 continue;
             }
-
-            computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false, false);
+            connectionConsumer.accept(cr, service);
         }
 
         final ProcessProviderRecord ppr = app.mProviders;
@@ -1021,15 +1006,51 @@
             ProcessRecord provider = cpc.provider.proc;
             if (provider == null || provider == app
                     || (provider.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ
-                            && provider.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
+                    && provider.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
                     || (provider.mState.getCurAdj() <= FOREGROUND_APP_ADJ
-                            && provider.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
-                            && provider.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+                    && provider.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
+                    && provider.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
                 continue;
             }
+            connectionConsumer.accept(cpc, provider);
+        }
+    }
 
-            computeProviderHostOomAdjLSP(cpc, provider, app, now, topApp, fullUpdate, false, false,
-                    oomAdjReason, cachedAdj, false, false);
+    /**
+     * Stream the connections from clients with {@code app} as the host to {@code
+     * connectionConsumer}.
+     */
+    @GuardedBy({"mService", "mProcLock"})
+    private static void forEachClientConnectionLSP(ProcessRecord app,
+            BiConsumer<Connection, ProcessRecord> connectionConsumer) {
+        final ProcessServiceRecord psr = app.mServices;
+
+        for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) {
+            final ServiceRecord s = psr.getRunningServiceAt(i);
+            final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
+                    s.getConnections();
+            for (int j = serviceConnections.size() - 1; j >= 0; j--) {
+                final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
+                for (int k = clist.size() - 1; k >= 0; k--) {
+                    final ConnectionRecord cr = clist.get(k);
+                    final ProcessRecord client;
+                    if (app.isSdkSandbox && cr.binding.attributedClient != null) {
+                        client = cr.binding.attributedClient;
+                    } else {
+                        client = cr.binding.client;
+                    }
+                    connectionConsumer.accept(cr, client);
+                }
+            }
+        }
+
+        final ProcessProviderRecord ppr = app.mProviders;
+        for (int i = ppr.numberOfProviders() - 1; i >= 0; i--) {
+            final ContentProviderRecord cpr = ppr.getProviderAt(i);
+            for (int j = cpr.connections.size() - 1; j >= 0; j--) {
+                final ContentProviderConnection conn = cpr.connections.get(j);
+                connectionConsumer.accept(conn, conn.client);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 2ef433c..0aa1a69 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -718,9 +718,7 @@
                         mService.mContext, mApp.info.packageName, mApp.info.flags);
             }
         }
-        for (BroadcastQueue queue : mService.mBroadcastQueues) {
-            queue.onApplicationProblemLocked(mApp);
-        }
+        mService.getBroadcastQueue().onApplicationProblemLocked(mApp);
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 89c8994..a1fdd50 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -208,7 +208,7 @@
 
     // Number of levels we have available for different service connection group importance
     // levels.
-    static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
+    public static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7009bd0..7356588 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -69,10 +69,8 @@
 import com.android.server.wm.WindowProcessListener;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Full information about a particular process that
@@ -1659,34 +1657,4 @@
                 && !mOptRecord.shouldNotFreeze()
                 && mState.getCurAdj() >= ProcessList.FREEZER_CUTOFF_ADJ;
     }
-
-    /**
-     * Traverses all client processes and feed them to consumer.
-     */
-    @GuardedBy("mProcLock")
-    void forEachClient(@NonNull Consumer<ProcessRecord> consumer) {
-        for (int i = mServices.numberOfRunningServices() - 1; i >= 0; i--) {
-            final ServiceRecord s = mServices.getRunningServiceAt(i);
-            final ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
-                    s.getConnections();
-            for (int j = serviceConnections.size() - 1; j >= 0; j--) {
-                final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
-                for (int k = clist.size() - 1; k >= 0; k--) {
-                    final ConnectionRecord cr = clist.get(k);
-                    if (isSdkSandbox && cr.binding.attributedClient != null) {
-                        consumer.accept(cr.binding.attributedClient);
-                    } else {
-                        consumer.accept(cr.binding.client);
-                    }
-                }
-            }
-        }
-        for (int i = mProviders.numberOfProviders() - 1; i >= 0; i--) {
-            final ContentProviderRecord cpr = mProviders.getProviderAt(i);
-            for (int j = cpr.connections.size() - 1; j >= 0; j--) {
-                final ContentProviderConnection conn = cpr.connections.get(j);
-                consumer.accept(conn.client);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 562beaf..3d695bc 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -541,7 +541,7 @@
     private void removeSdkSandboxConnectionIfNecessary(ConnectionRecord connection) {
         final ProcessRecord attributedClient = connection.binding.attributedClient;
         if (attributedClient != null && connection.binding.service.isSdkSandbox) {
-            if (attributedClient.mServices.mSdkSandboxConnections == null) {
+            if (attributedClient.mServices.mSdkSandboxConnections != null) {
                 attributedClient.mServices.mSdkSandboxConnections.remove(connection);
             }
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3abfe082..13a1807 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1419,7 +1419,8 @@
 
     private boolean allowBiometricUnlockForPrivateProfile() {
         return android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace();
+                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
     /**
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 68b4e3f..7ee2a7a 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
@@ -149,11 +149,7 @@
             return proto.getBytes();
         }
 
-        @android.annotation.EnforcePermission(
-                anyOf = {
-                        android.Manifest.permission.USE_BIOMETRIC_INTERNAL,
-                        android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION
-                })
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
         public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(
                 String opPackageName) {
@@ -297,29 +293,6 @@
                     restricted, statsClient, isKeyguard);
         }
 
-        @android.annotation.EnforcePermission(
-                android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION)
-        @Override // Binder call
-        public long authenticateInBackground(final IBinder token, final long operationId,
-                final IFaceServiceReceiver receiver, final FaceAuthenticateOptions options) {
-            // TODO(b/152413782): If the sensor supports face detect and the device is encrypted or
-            //  lockdown, something wrong happened. See similar path in FingerprintService.
-
-            super.authenticateInBackground_enforcePermission();
-
-            final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
-            if (provider == null) {
-                Slog.w(TAG, "Null provider for authenticate");
-                return -1;
-            }
-            options.setSensorId(provider.first);
-
-            return provider.second.scheduleAuthenticate(token, operationId,
-                    0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
-                    false /* restricted */, BiometricsProtoEnums.CLIENT_UNKNOWN /* statsClient */,
-                    true /* allowBackgroundAuthentication */);
-        }
-
         @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
         public long detectFace(final IBinder token,
@@ -583,11 +556,7 @@
             return provider.getEnrolledFaces(sensorId, userId);
         }
 
-        @android.annotation.EnforcePermission(
-                anyOf = {
-                        android.Manifest.permission.USE_BIOMETRIC_INTERNAL,
-                        android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION
-                })
+        @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
         @Override // Binder call
         public boolean hasEnrolledFaces(int sensorId, int userId, String opPackageName) {
             super.hasEnrolledFaces_enforcePermission();
diff --git a/services/core/java/com/android/server/clipboard/ArcClipboardMonitor.java b/services/core/java/com/android/server/clipboard/ArcClipboardMonitor.java
new file mode 100644
index 0000000..413020e
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/ArcClipboardMonitor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.clipboard;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+
+import com.android.server.LocalServices;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class ArcClipboardMonitor implements Consumer<ClipData> {
+    private static final String TAG = "ArcClipboardMonitor";
+
+    public interface ArcClipboardBridge {
+        /**
+         * Called when a clipboard content is updated.
+         */
+        void onPrimaryClipChanged(ClipData data);
+
+        /**
+         * Passes the callback to set a new clipboard content with a uid.
+         */
+        void setHandler(BiConsumer<ClipData, Integer> setAndroidClipboard);
+    }
+
+    private ArcClipboardBridge mBridge;
+    private BiConsumer<ClipData, Integer> mAndroidClipboardSetter;
+
+    ArcClipboardMonitor(final BiConsumer<ClipData, Integer> setAndroidClipboard) {
+        mAndroidClipboardSetter = setAndroidClipboard;
+        LocalServices.addService(ArcClipboardMonitor.class, this);
+    }
+
+    @Override
+    public void accept(final @Nullable ClipData clip) {
+        if (mBridge != null) {
+            mBridge.onPrimaryClipChanged(clip);
+        }
+    }
+
+    /**
+     * Sets the other end of the clipboard bridge.
+     */
+    public void setClipboardBridge(ArcClipboardBridge bridge) {
+        mBridge = bridge;
+        mBridge.setHandler(mAndroidClipboardSetter);
+    }
+}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 49f6070..4c3020f 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -151,7 +151,7 @@
     private final ContentCaptureManagerInternal mContentCaptureInternal;
     private final AutofillManagerInternal mAutofillInternal;
     private final IBinder mPermissionOwner;
-    private final Consumer<ClipData> mEmulatorClipboardMonitor;
+    private final Consumer<ClipData> mClipboardMonitor;
     private final Handler mWorkerHandler;
 
     @GuardedBy("mLock")
@@ -192,7 +192,7 @@
         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (Build.IS_EMULATOR) {
-            mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+            mClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
                 synchronized (mLock) {
                     Clipboard clipboard = getClipboardLocked(0, DEVICE_ID_DEFAULT);
                     if (clipboard != null) {
@@ -201,8 +201,12 @@
                     }
                 }
             });
+        } else if (Build.IS_ARC) {
+            mClipboardMonitor = new ArcClipboardMonitor((clip, uid) -> {
+                setPrimaryClipInternal(clip, uid);
+            });
         } else {
-            mEmulatorClipboardMonitor = (clip) -> {};
+            mClipboardMonitor = (clip) -> {};
         }
 
         updateConfig();
@@ -937,7 +941,7 @@
     private void setPrimaryClipInternalLocked(
             @Nullable ClipData clip, int uid, int deviceId, @Nullable String sourcePackage) {
         if (deviceId == DEVICE_ID_DEFAULT) {
-            mEmulatorClipboardMonitor.accept(clip);
+            mClipboardMonitor.accept(clip);
         }
 
         final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING
index f508319..55601bc 100644
--- a/services/core/java/com/android/server/connectivity/TEST_MAPPING
+++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING
@@ -8,6 +8,19 @@
               }
           ],
           "file_patterns": ["VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+      },
+      {
+          "name":"FrameworksVpnTests",
+          "options": [
+              {
+                "exclude-annotation": "com.android.testutils.SkipPresubmit"
+              }
+          ],
+          "file_patterns":[
+              "Vpn\\.java",
+              "VpnIkeV2Utils\\.java",
+              "VpnProfileStore\\.java"
+          ]
       }
   ],
   "presubmit-large": [
@@ -26,10 +39,5 @@
           ],
         "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
       }
-  ],
-  "postsubmit":[
-    {
-      "name":"FrameworksVpnTests"
-    }
   ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index a23c08a..d876a38 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -198,7 +198,7 @@
         addValidationInfo(Constants.MESSAGE_CEC_VERSION,
                 oneByteValidator, ADDR_NOT_UNREGISTERED, ADDR_DIRECT);
         addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
-                new AsciiValidator(3), ADDR_NOT_UNREGISTERED, ADDR_BROADCAST);
+                new AsciiValidator(3), ADDR_TV, ADDR_BROADCAST);
 
         ParameterValidator statusRequestValidator = new MinimumOneByteRangeValidator(0x01, 0x03);
         addValidationInfo(Constants.MESSAGE_DECK_CONTROL,
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c8c66238..d0532b99 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4665,15 +4665,27 @@
         public void onAudioDeviceVolumeChanged(
                 @NonNull AudioDeviceAttributes audioDevice,
                 @NonNull VolumeInfo volumeInfo) {
+            int localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress();
 
-            // Do nothing if the System Audio device does not support <Set Audio Volume Level>
+            // We can't send <Set Audio Volume Level> if the System Audio device doesn't support it.
+            // But AudioService has already updated its volume and expects us to set it.
+            // So the best we can do is to send <Give Audio Status>, which triggers
+            // <Report Audio Status>, which should update AudioService with its correct volume.
             if (mSystemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
                     != DeviceFeatures.FEATURE_SUPPORTED) {
+                // Update the volume tracked in AbsoluteVolumeAudioStatusAction
+                // so it correctly processes the next <Report Audio Status>
+                HdmiCecLocalDevice avbDevice = isTvDevice() ? tv() : playback();
+                avbDevice.updateAvbVolume(volumeInfo.getVolumeIndex());
+                // Send <Give Audio Status>
+                sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
+                        localDeviceAddress,
+                        mSystemAudioDevice.getLogicalAddress()
+                ));
                 return;
             }
 
             // Send <Set Audio Volume Level> to notify the System Audio device of the volume change
-            int localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress();
             sendCecCommand(SetAudioVolumeLevelMessage.build(
                             localDeviceAddress,
                             mSystemAudioDevice.getLogicalAddress(),
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 29ea071..c80f988 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -814,7 +814,8 @@
         // storage is locked, instead of when the user is stopped.  This would ensure the flags get
         // reset if CE storage is locked later for a user that allows delayed locking.
         if (android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             UserProperties userProperties = mUserManager.getUserProperties(UserHandle.of(userId));
             if (userProperties != null && userProperties.getAllowStoppingUserWithDelayedLocking()) {
                 return;
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 8db5905..4fc1a17 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -27,11 +27,15 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
-    }
-  ],
-  "postsubmit":[
+    },
     {
-      "name":"FrameworksVpnTests"
+      "name": "FrameworksVpnTests",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        }
+      ],
+      "file_patterns": ["VpnManagerService\\.java"]
     }
   ]
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ba5882c..b98424c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1204,6 +1204,10 @@
         }
     }
 
+    private static boolean privateSpaceFlagsEnabled() {
+        return allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures();
+    }
+
     private final class SavePolicyFileRunnable implements Runnable {
         @Override
         public void run() {
@@ -2142,7 +2146,7 @@
         }
 
         private boolean isProfileUnavailable(String action) {
-            return allowPrivateProfile() ?
+            return privateSpaceFlagsEnabled() ?
                     action.equals(Intent.ACTION_PROFILE_UNAVAILABLE) :
                     action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         }
@@ -2744,7 +2748,7 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
-        if (allowPrivateProfile()){
+        if (privateSpaceFlagsEnabled()){
             filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
         }
         getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index bc86c82..289faf4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -35,6 +35,8 @@
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.Preconditions.checkArgument;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -536,36 +538,40 @@
     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
             @ConfigChangeOrigin int origin, String reason, int callingUid) {
         requirePublicOrigin("updateAutomaticZenRule", origin);
-        ZenModeConfig newConfig;
+        if (ruleId == null) {
+            throw new IllegalArgumentException("ruleId cannot be null");
+        }
         synchronized (mConfigLock) {
             if (mConfig == null) return false;
             if (DEBUG) {
                 Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
                         + " reason=" + reason);
             }
-            newConfig = mConfig.copy();
-            ZenModeConfig.ZenRule rule;
-            if (ruleId == null) {
-                throw new IllegalArgumentException("Rule doesn't exist");
-            } else {
-                rule = newConfig.automaticRules.get(ruleId);
-                if (rule == null || !canManageAutomaticZenRule(rule)) {
-                    throw new SecurityException(
-                            "Cannot update rules not owned by your condition provider");
-                }
+            ZenModeConfig.ZenRule oldRule = mConfig.automaticRules.get(ruleId);
+            if (oldRule == null || !canManageAutomaticZenRule(oldRule)) {
+                throw new SecurityException(
+                        "Cannot update rules not owned by your condition provider");
             }
+            ZenModeConfig newConfig = mConfig.copy();
+            ZenModeConfig.ZenRule newRule = requireNonNull(newConfig.automaticRules.get(ruleId));
             if (!Flags.modesApi()) {
-                if (rule.enabled != automaticZenRule.isEnabled()) {
-                    dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.getPkg(), ruleId,
+                if (newRule.enabled != automaticZenRule.isEnabled()) {
+                    dispatchOnAutomaticRuleStatusChanged(mConfig.user, newRule.getPkg(), ruleId,
                             automaticZenRule.isEnabled()
                                     ? AUTOMATIC_RULE_STATUS_ENABLED
                                     : AUTOMATIC_RULE_STATUS_DISABLED);
                 }
             }
 
-            populateZenRule(rule.pkg, automaticZenRule, rule, origin, /* isNew= */ false);
+            boolean updated = populateZenRule(newRule.pkg, automaticZenRule, newRule,
+                    origin, /* isNew= */ false);
+            if (Flags.modesApi() && !updated) {
+                // Bail out so we don't have the side effects of updating a rule (i.e. dropping
+                // condition) when no changes happen.
+                return true;
+            }
             return setConfigLocked(newConfig, origin, reason,
-                    rule.component, true, callingUid);
+                    newRule.component, true, callingUid);
         }
     }
 
@@ -1073,31 +1079,67 @@
         return null;
     }
 
-    private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+    /**
+     * Populates a {@code ZenRule} with the content of the {@link AutomaticZenRule}. Can be used for
+     * both rule creation or update (distinguished by the {@code isNew} parameter. The change is
+     * applied differently depending on the origin; for example app-provided changes might be
+     * ignored (if the rule was previously customized by the user), while user-provided changes
+     * update the user-modified bitmasks for any modifications.
+     *
+     * <p>Returns {@code true} if the rule was modified. Note that this is not equivalent to
+     * {@link ZenRule#equals} or {@link AutomaticZenRule#equals}, for various reasons:
+     * <ul>
+     *     <li>some metadata-related fields are not considered
+     *     <li>some fields (like {@code condition} are always reset, and ignored for this result
+     *     <li>an app may provide changes that are not actually applied, as described above
+     * </ul>
+     */
+    private boolean populateZenRule(String pkg, AutomaticZenRule azr, ZenRule rule,
                          @ConfigChangeOrigin int origin, boolean isNew) {
         if (Flags.modesApi()) {
+            boolean modified = false;
             // These values can always be edited by the app, so we apply changes immediately.
             if (isNew) {
                 rule.id = ZenModeConfig.newRuleId();
                 rule.creationTime = mClock.millis();
-                rule.component = automaticZenRule.getOwner();
+                rule.component = azr.getOwner();
                 rule.pkg = pkg;
+                modified = true;
             }
 
             rule.condition = null;
-            rule.conditionId = automaticZenRule.getConditionId();
-            if (rule.enabled != automaticZenRule.isEnabled()) {
-                rule.snoozing = false;
+            if (!Objects.equals(rule.conditionId, azr.getConditionId())) {
+                rule.conditionId = azr.getConditionId();
+                modified = true;
             }
-            rule.enabled = automaticZenRule.isEnabled();
-            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
-            rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
-            rule.iconResName =
-                    drawableResIdToResName(rule.pkg, automaticZenRule.getIconResId());
-            rule.triggerDescription = automaticZenRule.getTriggerDescription();
-            rule.type = automaticZenRule.getType();
+            if (rule.enabled != azr.isEnabled()) {
+                rule.enabled = azr.isEnabled();
+                rule.snoozing = false;
+                modified = true;
+            }
+            if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
+                rule.configurationActivity = azr.getConfigurationActivity();
+                modified = true;
+            }
+            if (rule.allowManualInvocation != azr.isManualInvocationAllowed()) {
+                rule.allowManualInvocation = azr.isManualInvocationAllowed();
+                modified = true;
+            }
+            String iconResName = drawableResIdToResName(rule.pkg, azr.getIconResId());
+            if (!Objects.equals(rule.iconResName, iconResName)) {
+                rule.iconResName = iconResName;
+                modified = true;
+            }
+            if (!Objects.equals(rule.triggerDescription, azr.getTriggerDescription())) {
+                rule.triggerDescription = azr.getTriggerDescription();
+                modified = true;
+            }
+            if (rule.type != azr.getType()) {
+                rule.type = azr.getType();
+                modified = true;
+            }
             // TODO: b/310620812 - Remove this once FLAG_MODES_API is inlined.
-            rule.modified = automaticZenRule.isModified();
+            rule.modified = azr.isModified();
 
             // Name is treated differently than other values:
             // App is allowed to update name if the name was not modified by the user (even if
@@ -1107,7 +1149,8 @@
             String previousName = rule.name;
             if (isNew || doesOriginAlwaysUpdateValues(origin)
                     || (rule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0) {
-                rule.name = automaticZenRule.getName();
+                rule.name = azr.getName();
+                modified |= !Objects.equals(rule.name, previousName);
             }
 
             // For the remaining values, rules can always have all values updated if:
@@ -1119,50 +1162,56 @@
 
             // For all other values, if updates are not allowed, we discard the update.
             if (!updateValues) {
-                return;
+                return modified;
             }
 
             // Updates the bitmasks if the origin of the change is the user.
             boolean updateBitmask = (origin == UPDATE_ORIGIN_USER);
 
-            if (updateBitmask && !TextUtils.equals(previousName, automaticZenRule.getName())) {
+            if (updateBitmask && !TextUtils.equals(previousName, azr.getName())) {
                 rule.userModifiedFields |= AutomaticZenRule.FIELD_NAME;
             }
             int newZenMode = NotificationManager.zenModeFromInterruptionFilter(
-                    automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
-            if (updateBitmask && rule.zenMode != newZenMode) {
-                rule.userModifiedFields |= AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+                    azr.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+            if (rule.zenMode != newZenMode) {
+                rule.zenMode = newZenMode;
+                if (updateBitmask) {
+                    rule.userModifiedFields |= AutomaticZenRule.FIELD_INTERRUPTION_FILTER;
+                }
+                modified = true;
             }
 
-            // Updates the values in the ZenRule itself.
-            rule.zenMode = newZenMode;
-
             // Updates the bitmask and values for all policy fields, based on the origin.
-            updatePolicy(rule, automaticZenRule.getZenPolicy(), updateBitmask, isNew);
+            modified |= updatePolicy(rule, azr.getZenPolicy(), updateBitmask, isNew);
 
             // Updates the bitmask and values for all device effect fields, based on the origin.
-            updateZenDeviceEffects(rule, automaticZenRule.getDeviceEffects(),
+            modified |= updateZenDeviceEffects(rule, azr.getDeviceEffects(),
                     origin == UPDATE_ORIGIN_APP, updateBitmask);
+
+            return modified;
         } else {
-            if (rule.enabled != automaticZenRule.isEnabled()) {
+            if (rule.enabled != azr.isEnabled()) {
                 rule.snoozing = false;
             }
-            rule.name = automaticZenRule.getName();
+            rule.name = azr.getName();
             rule.condition = null;
-            rule.conditionId = automaticZenRule.getConditionId();
-            rule.enabled = automaticZenRule.isEnabled();
-            rule.modified = automaticZenRule.isModified();
-            rule.zenPolicy = automaticZenRule.getZenPolicy();
+            rule.conditionId = azr.getConditionId();
+            rule.enabled = azr.isEnabled();
+            rule.modified = azr.isModified();
+            rule.zenPolicy = azr.getZenPolicy();
             rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
-                    automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
-            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+                    azr.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+            rule.configurationActivity = azr.getConfigurationActivity();
 
             if (isNew) {
                 rule.id = ZenModeConfig.newRuleId();
                 rule.creationTime = System.currentTimeMillis();
-                rule.component = automaticZenRule.getOwner();
+                rule.component = azr.getOwner();
                 rule.pkg = pkg;
             }
+
+            // Only the MODES_API path cares about the result, so just return whatever here.
+            return true;
         }
     }
 
@@ -1182,16 +1231,19 @@
      * provided {@code ZenRule}, keeping any pre-existing settings from {@code zenRule.zenPolicy}
      * for any unset policy fields in {@code newPolicy}. The user-modified bitmask is updated to
      * reflect the changes being applied (if applicable, i.e. if the update is from the user).
+     *
+     * <p>Returns {@code true} if the policy of the rule was modified.
      */
-    private void updatePolicy(ZenRule zenRule, @Nullable ZenPolicy newPolicy,
+    private boolean updatePolicy(ZenRule zenRule, @Nullable ZenPolicy newPolicy,
             boolean updateBitmask, boolean isNew) {
         if (newPolicy == null) {
             if (isNew) {
                 // Newly created rule with no provided policy; fill in with the default.
                 zenRule.zenPolicy = mDefaultConfig.toZenPolicy();
+                return true;
             }
             // Otherwise, a null policy means no policy changes, so we can stop here.
-            return;
+            return false;
         }
 
         // If oldPolicy is null, we compare against the default policy when determining which
@@ -1272,6 +1324,8 @@
             }
             zenRule.zenPolicyUserModifiedFields = userModifiedFields;
         }
+
+        return !newPolicy.equals(oldPolicy);
     }
 
     /**
@@ -1283,12 +1337,14 @@
      * <p>Apps cannot turn on hidden effects (those tagged as {@code @hide}), so those fields are
      * treated especially: for a new rule, they are blanked out; for an updated rule, previous
      * values are preserved.
+     *
+     * <p>Returns {@code true} if the device effects of the rule were modified.
      */
-    private static void updateZenDeviceEffects(ZenRule zenRule,
+    private static boolean updateZenDeviceEffects(ZenRule zenRule,
             @Nullable ZenDeviceEffects newEffects, boolean isFromApp, boolean updateBitmask) {
         // Same as with ZenPolicy, supplying null effects means keeping the previous ones.
         if (newEffects == null) {
-            return;
+            return false;
         }
 
         ZenDeviceEffects oldEffects = zenRule.zenDeviceEffects != null
@@ -1349,6 +1405,8 @@
             }
             zenRule.zenDeviceEffectsUserModifiedFields = userModifiedFields;
         }
+
+        return !newEffects.equals(oldEffects);
     }
 
     private AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
@@ -2505,7 +2563,7 @@
         if (resId == 0) {
             return null;
         }
-        Objects.requireNonNull(packageName);
+        requireNonNull(packageName);
         try {
             final Resources res = mPm.getResourcesForApplication(packageName);
             return res.getResourceName(resId);
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 23d48e8..9af2b3f 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -389,7 +389,8 @@
      */
     boolean canLauncherAccessProfile(ComponentName launcherComponent, int userId) {
         if (android.os.Flags.allowPrivateProfile()
-                && Flags.enablePermissionToAccessHiddenProfiles()) {
+                && Flags.enablePermissionToAccessHiddenProfiles()
+                && Flags.enablePrivateSpaceFeatures()) {
             if (mUmInternal.getUserProperties(userId).getProfileApiVisibility()
                     != UserProperties.PROFILE_API_VISIBILITY_HIDDEN) {
                 return true;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6b56b85..c7ebb3c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -584,7 +584,8 @@
             return android.os.Flags.allowPrivateProfile()
                     && Flags.enableHidingProfiles()
                     && Flags.enableLauncherAppsHiddenProfileChecks()
-                    && Flags.enablePermissionToAccessHiddenProfiles();
+                    && Flags.enablePermissionToAccessHiddenProfiles()
+                    && Flags.enablePrivateSpaceFeatures();
         }
 
         @VisibleForTesting // We override it in unit tests
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 211b754..4c653f6 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2833,7 +2833,8 @@
 
     @VisibleForTesting
     boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId) {
-        if (!android.os.Flags.allowPrivateProfile() || !Flags.disablePrivateSpaceItemsOnHome()) {
+        if (!android.os.Flags.allowPrivateProfile() || !Flags.disablePrivateSpaceItemsOnHome()
+                || !android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             return true;
         }
         final long start = getStatStartTime();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7349755..88e7596 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -21,10 +21,15 @@
 import static android.content.Intent.EXTRA_USER_ID;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
 import static android.os.UserManager.DISALLOW_USER_SWITCH;
 import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
 import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
+import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
 
 import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
 import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
@@ -1006,9 +1011,17 @@
         emulateSystemUserModeIfNeeded();
     }
 
+    private boolean doesDeviceHardwareSupportPrivateSpace() {
+        return !mPm.hasSystemFeature(FEATURE_EMBEDDED, 0)
+                && !mPm.hasSystemFeature(FEATURE_WATCH, 0)
+                && !mPm.hasSystemFeature(FEATURE_LEANBACK, 0)
+                && !mPm.hasSystemFeature(FEATURE_AUTOMOTIVE, 0);
+    }
+
     private static boolean isAutoLockForPrivateSpaceEnabled() {
         return android.os.Flags.allowPrivateProfile()
-                && Flags.supportAutolockForPrivateSpace();
+                && Flags.supportAutolockForPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
     void systemReady() {
@@ -1052,7 +1065,8 @@
 
     private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
         return android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enablePrivateSpaceAutolockOnRestarts();
+                && android.multiuser.Flags.enablePrivateSpaceAutolockOnRestarts()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures();
     }
 
     /**
@@ -1493,7 +1507,8 @@
     private boolean isProfileHidden(int userId) {
         UserProperties userProperties = getUserPropertiesCopy(userId);
         if (android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enableHidingProfiles()) {
+                && android.multiuser.Flags.enableHidingProfiles()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             return userProperties.getProfileApiVisibility()
                     == UserProperties.PROFILE_API_VISIBILITY_HIDDEN;
         }
@@ -1693,7 +1708,8 @@
                 setQuietModeEnabled(userId, true /* enableQuietMode */, target, callingPackage);
                 return true;
             }
-            if (android.os.Flags.allowPrivateProfile()) {
+            if (android.os.Flags.allowPrivateProfile()
+                    && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
                 final UserProperties userProperties = getUserPropertiesInternal(userId);
                 if (userProperties != null
                         && userProperties.isAuthAlwaysRequiredToDisableQuietMode()) {
@@ -1839,7 +1855,8 @@
         logQuietModeEnabled(userId, enableQuietMode, callingPackage);
 
         // Broadcast generic intents for all profiles
-        if (android.os.Flags.allowPrivateProfile()) {
+        if (android.os.Flags.allowPrivateProfile()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             broadcastProfileAvailabilityChanges(profile, parent.getUserHandle(),
                     enableQuietMode, false);
         }
@@ -1852,7 +1869,8 @@
 
     private void stopUserForQuietMode(int userId) throws RemoteException {
         if (android.os.Flags.allowPrivateProfile()
-                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) {
+                && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()
+                && android.multiuser.Flags.enablePrivateSpaceFeatures()) {
             // Allow delayed locking since some profile types want to be able to unlock again via
             // biometrics.
             ActivityManager.getService()
@@ -2751,6 +2769,18 @@
     }
 
     @Override
+    public boolean canAddPrivateProfile(@UserIdInt int userId) {
+        checkCreateUsersPermission("canHaveRestrictedProfile");
+        UserInfo parentUserInfo = getUserInfo(userId);
+        return isUserTypeEnabled(USER_TYPE_PROFILE_PRIVATE)
+                && canAddMoreProfilesToUser(USER_TYPE_PROFILE_PRIVATE,
+                    userId, /* allowedToRemoveOne */ false)
+                && (parentUserInfo != null && parentUserInfo.isMain())
+                && doesDeviceHardwareSupportPrivateSpace()
+                && !hasUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE, userId);
+    }
+
+    @Override
     public boolean hasRestrictedProfiles(@UserIdInt int userId) {
         checkManageUsersPermission("hasRestrictedProfiles");
         synchronized (mUsersLock) {
@@ -5308,7 +5338,7 @@
         if (!isUserTypeEnabled(userTypeDetails)) {
             throwCheckedUserOperationException(
                     "Cannot add a user of disabled type " + userType + ".",
-                    UserManager.USER_OPERATION_ERROR_MAX_USERS);
+                    UserManager.USER_OPERATION_ERROR_DISABLED_USER);
         }
 
         synchronized (mUsersLock) {
@@ -5341,6 +5371,7 @@
         final boolean isDemo = UserManager.isUserTypeDemo(userType);
         final boolean isManagedProfile = UserManager.isUserTypeManagedProfile(userType);
         final boolean isCommunalProfile = UserManager.isUserTypeCommunalProfile(userType);
+        final boolean isPrivateProfile = UserManager.isUserTypePrivateProfile(userType);
 
         final long ident = Binder.clearCallingIdentity();
         UserInfo userInfo;
@@ -5387,6 +5418,12 @@
                                     + " for user " + parentId,
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
+                if (android.multiuser.Flags.blockPrivateSpaceCreation()
+                        && isPrivateProfile && !canAddPrivateProfile(parentId)) {
+                    throwCheckedUserOperationException(
+                            "Cannot add profile of type " + userType + " for user " + parentId,
+                            UserManager.USER_OPERATION_ERROR_PRIVATE_PROFILE);
+                }
                 if (isRestricted && (parentId != UserHandle.USER_SYSTEM)
                         && !isCreationOverrideEnabled()) {
                     throwCheckedUserOperationException(
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 114daaa..7f9c1cf 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -292,6 +292,7 @@
                 .setName(USER_TYPE_PROFILE_PRIVATE)
                 .setBaseType(FLAG_PROFILE)
                 .setMaxAllowedPerParent(1)
+                .setEnabled(UserManager.isPrivateProfileEnabled() ? 1 : 0)
                 .setLabels(R.string.profile_label_private)
                 .setIconBadge(com.android.internal.R.drawable.ic_private_profile_icon_badge)
                 .setBadgePlain(com.android.internal.R.drawable.ic_private_profile_badge)
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 305b087..5c8215e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -16,6 +16,10 @@
 
 package com.android.server.pm.verify.domain;
 
+import static android.content.IntentFilter.WILDCARD;
+
+import static com.android.server.pm.verify.domain.DomainVerificationUtils.isValidDomain;
+
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
 
@@ -253,9 +257,18 @@
             Map<String, List<UriRelativeFilterGroup>> domainToGroupsMap =
                     pkgState.getUriRelativeFilterGroupMap();
             for (String domain : bundle.keySet()) {
+                if (!isValidDomain(domain)) {
+                    continue;
+                }
                 ArrayList<UriRelativeFilterGroupParcel> parcels =
                         bundle.getParcelableArrayList(domain, UriRelativeFilterGroupParcel.class);
-                domainToGroupsMap.put(domain, UriRelativeFilterGroup.parcelsToGroups(parcels));
+                List<UriRelativeFilterGroup> groups =
+                        UriRelativeFilterGroup.parcelsToGroups(parcels);
+                if (groups == null || groups.isEmpty()) {
+                    domainToGroupsMap.remove(domain);
+                } else {
+                    domainToGroupsMap.put(domain, groups);
+                }
             }
         }
     }
@@ -273,9 +286,11 @@
                 Map<String, List<UriRelativeFilterGroup>> map =
                         pkgState.getUriRelativeFilterGroupMap();
                 for (int i = 0; i < domains.size(); i++) {
-                    List<UriRelativeFilterGroup> groups = map.get(domains.get(i));
-                    bundle.putParcelableList(domains.get(i),
-                            UriRelativeFilterGroup.groupsToParcels(groups));
+                    if (map.containsKey(domains.get(i))) {
+                        List<UriRelativeFilterGroup> groups = map.get(domains.get(i));
+                        bundle.putParcelableList(domains.get(i),
+                                UriRelativeFilterGroup.groupsToParcels(groups));
+                    }
                 }
             }
         }
@@ -285,15 +300,29 @@
     @NonNull
     private List<UriRelativeFilterGroup> getUriRelativeFilterGroups(@NonNull String packageName,
             @NonNull String domain) {
-        List<UriRelativeFilterGroup> groups = Collections.emptyList();
+        List<UriRelativeFilterGroup> groups;
         synchronized (mLock) {
             DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
             if (pkgState != null) {
-                groups = pkgState.getUriRelativeFilterGroupMap().getOrDefault(domain,
-                        Collections.emptyList());
+                Map<String, List<UriRelativeFilterGroup>> groupMap =
+                        pkgState.getUriRelativeFilterGroupMap();
+                groups = groupMap.get(domain);
+                if (groups != null) {
+                    return groups;
+                }
+                int first = domain.indexOf(".");
+                int second = domain.indexOf('.', first + 1);
+                while (first > 0 && second > 0) {
+                    groups = groupMap.get(WILDCARD + domain.substring(first));
+                    if (groups != null) {
+                        return groups;
+                    }
+                    first = second;
+                    second = domain.indexOf('.', second + 1);
+                }
             }
         }
-        return groups;
+        return Collections.emptyList();
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 3fd00c6..b8c4d22 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -35,6 +35,9 @@
 
 public final class DomainVerificationUtils {
 
+    public static final int MAX_DOMAIN_LENGTH = 254;
+    public static final int MAX_DOMAIN_LABEL_LENGTH = 63;
+
     private static final ThreadLocal<Matcher> sCachedMatcher = ThreadLocal.withInitial(
             () -> Patterns.DOMAIN_NAME.matcher(""));
 
@@ -108,4 +111,41 @@
         appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
         return appInfo;
     }
+
+    static boolean isValidDomain(String domain) {
+        if (domain.length() > MAX_DOMAIN_LENGTH || domain.equals("*")) {
+            return false;
+        }
+        if (domain.charAt(0) == '*') {
+            if (domain.charAt(1) != '.') {
+                return false;
+            }
+            domain = domain.substring(2);
+        }
+        int labels = 1;
+        int labelStart = -1;
+        for (int i = 0; i < domain.length(); i++) {
+            char c = domain.charAt(i);
+            if (c == '.') {
+                int labelLength = i - labelStart - 1;
+                if (labelLength == 0 || labelLength > MAX_DOMAIN_LABEL_LENGTH) {
+                    return false;
+                }
+                labelStart = i;
+                labels += 1;
+            } else if (!isValidDomainChar(c)) {
+                return false;
+            }
+        }
+        int lastLabelLength = domain.length() - labelStart - 1;
+        if (lastLabelLength == 0 || lastLabelLength > 63) {
+            return false;
+        }
+        return labels > 1;
+    }
+
+    private static boolean isValidDomainChar(char c) {
+        return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+                || (c >= '0' && c <= '9') || c == '-';
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 5b77433..2fc183d 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -532,7 +532,7 @@
             return false;
         }
 
-        if (Flags.keyboardCategoryEnabled()) {
+        if (Flags.keyboardCategoryEnabled() && mVibrationConfig.hasFixedKeyboardAmplitude()) {
             int category = callerInfo.attrs.getCategory();
             if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
                 // Keyboard touch has a different user setting.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d30a216..7ba953d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -658,7 +658,7 @@
 
     boolean mVoiceInteraction;
 
-    private int mPendingRelaunchCount;
+    int mPendingRelaunchCount;
     long mRelaunchStartTime;
 
     // True if we are current in the process of removing this app token from the display
@@ -3988,7 +3988,7 @@
         // If the display does not have running activity, the configuration may need to be
         // updated for restoring original orientation of the display.
         if (next == null) {
-            mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+            mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent,
                     true /* deferResume */);
         }
         if (activityRemoved) {
@@ -6463,12 +6463,6 @@
      * state to match that fact.
      */
     void completeResumeLocked() {
-        final boolean wasVisible = mVisibleRequested;
-        setVisibility(true);
-        if (!wasVisible) {
-            // Visibility has changed, so take a note of it so we call the TaskStackChangedListener
-            mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
-        }
         idle = false;
         results = null;
         if (newIntents != null && newIntents.size() > 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 6ad056f..2c39c58 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1625,7 +1625,7 @@
         final ActivityRecord currentTop = startedActivityRootTask.topRunningActivity();
         if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
             mRootWindowContainer.ensureVisibilityAndConfig(
-                    currentTop, currentTop.getDisplayId(), false /* deferResume */);
+                    currentTop, currentTop.mDisplayContent, false /* deferResume */);
         }
 
         if (!avoidMoveToFront() && mDoResume && mRootWindowContainer
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 2cda1f5..826e332 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -842,7 +842,7 @@
                 // Deferring resume here because we're going to launch new activity shortly.
                 // We don't want to perform a redundant launch of the same record while ensuring
                 // configurations and trying to resume top activity of focused root task.
-                mRootWindowContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
+                mRootWindowContainer.ensureVisibilityAndConfig(r, r.mDisplayContent,
                         true /* deferResume */);
             }
 
@@ -1011,7 +1011,8 @@
         if (andResume && readyToResume()) {
             // As part of the process of launching, ActivityThread also performs
             // a resume.
-            rootTask.minimalResumeActivityLocked(r);
+            r.setState(RESUMED, "realStartActivityLocked");
+            r.completeResumeLocked();
         } else {
             // This activity is not starting in the resumed state... which should look like we asked
             // it to pause+stop (but remain visible), and it has done so and reported back the
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 07a03eb..a75d3b6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1742,30 +1742,21 @@
      *
      * @param starting                  The currently starting activity or {@code null} if there is
      *                                  none.
-     * @param displayId                 The id of the display where operation is executed.
+     * @param displayContent            The display where the operation is executed.
      * @param deferResume               Whether to defer resume while updating config.
-     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
-     * because of configuration update.
      */
-    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId, boolean deferResume) {
+    void ensureVisibilityAndConfig(@Nullable ActivityRecord starting,
+            @NonNull DisplayContent displayContent, boolean deferResume) {
         // First ensure visibility without updating the config just yet. We need this to know what
         // activities are affecting configuration now.
         // Passing null here for 'starting' param value, so that visibility of actual starting
         // activity will be properly updated.
         ensureActivitiesVisible(null /* starting */, false /* notifyClients */);
 
-        if (displayId == INVALID_DISPLAY) {
-            // The caller didn't provide a valid display id, skip updating config.
-            return true;
-        }
-
         // Force-update the orientation from the WindowManager, since we need the true configuration
         // to send to the client now.
-        final DisplayContent displayContent = getDisplayContent(displayId);
-        Configuration config = null;
-        if (displayContent != null) {
-            config = displayContent.updateOrientation(starting, true /* forceUpdate */);
-        }
+        final Configuration config =
+                displayContent.updateOrientation(starting, true /* forceUpdate */);
         // Visibilities may change so let the starting activity have a chance to report. Can't do it
         // when visibility is changed in each AppWindowToken because it may trigger wrong
         // configuration push because the visibility of some activities may not be updated yet.
@@ -1773,13 +1764,8 @@
             starting.reportDescendantOrientationChangeIfNeeded();
         }
 
-        if (displayContent != null) {
-            // Update the configuration of the activities on the display.
-            return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
-                    deferResume);
-        } else {
-            return true;
-        }
+        // Update the configuration of the activities on the display.
+        displayContent.updateDisplayOverrideConfigurationLocked(config, starting, deferResume);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e16d869..73aa307 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -978,7 +978,6 @@
         }
         effectiveUid = info.applicationInfo.uid;
         mIsEffectivelySystemApp = info.applicationInfo.isSystemApp();
-        stringName = null;
 
         if (info.targetActivity == null) {
             if (_intent != null) {
@@ -1045,6 +1044,7 @@
             updateTaskDescription();
         }
         mSupportsPictureInPicture = info.supportsPictureInPicture();
+        stringName = null;
 
         // Re-adding the task to Recents once updated
         if (inRecents) {
@@ -4948,13 +4948,6 @@
         }
     }
 
-    void minimalResumeActivityLocked(ActivityRecord r) {
-        ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (starting new instance) "
-                + "callers=%s", r, Debug.getCallers(5));
-        r.setState(RESUMED, "minimalResumeActivityLocked");
-        r.completeResumeLocked();
-    }
-
     void checkReadyForSleep() {
         if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
             mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -5863,7 +5856,7 @@
             }
 
             mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
-                    mDisplayContent.mDisplayId, false /* deferResume */);
+                    mDisplayContent, false /* deferResume */);
         } finally {
             if (mTransitionController.isShellTransitionsEnabled()) {
                 mAtmService.continueWindowLayout();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a818a72..597e901 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1536,10 +1536,6 @@
 
             next.setState(RESUMED, "resumeTopActivity");
 
-            // Have the window manager re-evaluate the orientation of
-            // the screen based on the new activity order.
-            boolean notUpdated = true;
-
             // Activity should also be visible if set mLaunchTaskBehind to true (see
             // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
             if (shouldBeVisible(next)) {
@@ -1551,28 +1547,15 @@
                 // result of invisible window resize.
                 // TODO: Remove this once visibilities are set correctly immediately when
                 // starting an activity.
-                notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+                final int originalRelaunchingCount = next.mPendingRelaunchCount;
+                mRootWindowContainer.ensureVisibilityAndConfig(next, mDisplayContent,
                         false /* deferResume */);
-            }
-
-            if (notUpdated) {
-                // The configuration update wasn't able to keep the existing
-                // instance of the activity, and instead started a new one.
-                // We should be all done, but let's just make sure our activity
-                // is still at the top and schedule another run if something
-                // weird happened.
-                ActivityRecord nextNext = topRunningActivity();
-                ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
-                        + "%s, new next: %s", next, nextNext);
-                if (nextNext != next) {
-                    // Do over!
-                    mTaskSupervisor.scheduleResumeTopActivities();
+                if (next.mPendingRelaunchCount > originalRelaunchingCount) {
+                    // The activity is scheduled to relaunch, then ResumeActivityItem will be also
+                    // included (see ActivityRecord#relaunchActivityLocked) if it should resume.
+                    next.completeResumeLocked();
+                    return true;
                 }
-                if (!next.isVisibleRequested() || next.mAppStopped) {
-                    next.setVisibility(true);
-                }
-                next.completeResumeLocked();
-                return true;
             }
 
             try {
@@ -1655,17 +1638,7 @@
                 return true;
             }
 
-            // From this point on, if something goes wrong there is no way
-            // to recover the activity.
-            try {
-                next.completeResumeLocked();
-            } catch (Exception e) {
-                // If any exception gets thrown, toss away this
-                // activity and try the next one.
-                Slog.w(TAG, "Exception thrown during resume of " + next, e);
-                next.finishIfPossible("resume-exception", true /* oomAdj */);
-                return true;
-            }
+            next.completeResumeLocked();
         } else {
             // Whoops, need to restart this activity!
             if (!next.hasBeenLaunched) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 7fc61e1..a84a99a 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -573,7 +573,7 @@
 
     // Capture the animation surface control for activity's main window
     static class StartingWindowAnimationAdaptor implements AnimationAdapter {
-        SurfaceControl mAnimationLeash;
+
         @Override
         public boolean getShowWallpaper() {
             return false;
@@ -582,14 +582,10 @@
         @Override
         public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
                 int type, @NonNull SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
-            mAnimationLeash = animationLeash;
         }
 
         @Override
         public void onAnimationCancelled(SurfaceControl animationLeash) {
-            if (mAnimationLeash == animationLeash) {
-                mAnimationLeash = null;
-            }
         }
 
         @Override
@@ -604,9 +600,6 @@
 
         @Override
         public void dump(PrintWriter pw, String prefix) {
-            pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
-            pw.print(mAnimationLeash);
-            pw.println();
         }
 
         @Override
@@ -616,16 +609,16 @@
 
     static SurfaceControl applyStartingWindowAnimation(WindowState window) {
         final SurfaceControl.Transaction t = window.getPendingTransaction();
-        final Rect mainFrame = window.getRelativeFrame();
         final StartingWindowAnimationAdaptor adaptor = new StartingWindowAnimationAdaptor();
         window.startAnimation(t, adaptor, false, ANIMATION_TYPE_STARTING_REVEAL);
-        if (adaptor.mAnimationLeash == null) {
+        final SurfaceControl leash = window.getAnimationLeash();
+        if (leash == null) {
             Slog.e(TAG, "Cannot start starting window animation, the window " + window
                     + " was removed");
             return null;
         }
-        t.setPosition(adaptor.mAnimationLeash, mainFrame.left, mainFrame.top);
-        return adaptor.mAnimationLeash;
+        t.setPosition(leash, window.mSurfacePosition.x, window.mSurfacePosition.y);
+        return leash;
     }
 
     boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
@@ -696,7 +689,9 @@
                 removalInfo.roundedCornerRadius =
                         topActivity.mLetterboxUiController.getRoundedCornersRadius(mainWindow);
                 removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
-                removalInfo.mainFrame = mainWindow.getRelativeFrame();
+                removalInfo.mainFrame = new Rect(mainWindow.getFrame());
+                removalInfo.mainFrame.offsetTo(mainWindow.mSurfacePosition.x,
+                        mainWindow.mSurfacePosition.y);
             }
         }
         try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 0b29f96..a6db310f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -49,6 +49,7 @@
 
 import com.android.internal.os.ByteTransferPipe;
 import com.android.internal.protolog.LegacyProtoLogImpl;
+import com.android.internal.protolog.PerfettoProtoLogImpl;
 import com.android.internal.protolog.common.IProtoLog;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.IoThread;
@@ -111,8 +112,13 @@
                 case "logging":
                     IProtoLog instance = ProtoLog.getSingleInstance();
                     int result = 0;
-                    if (instance instanceof LegacyProtoLogImpl) {
-                        result = ((LegacyProtoLogImpl) instance).onShellCommand(this);
+                    if (instance instanceof LegacyProtoLogImpl
+                            || instance instanceof PerfettoProtoLogImpl) {
+                        if (instance instanceof LegacyProtoLogImpl) {
+                            result = ((LegacyProtoLogImpl) instance).onShellCommand(this);
+                        } else {
+                            result = ((PerfettoProtoLogImpl) instance).onShellCommand(this);
+                        }
                         if (result != 0) {
                             pw.println("Not handled, please use "
                                     + "`adb shell dumpsys activity service SystemUIService "
@@ -120,8 +126,7 @@
                         }
                     } else {
                         result = -1;
-                        pw.println("Command not supported. "
-                                + "Only supported when using legacy ProtoLog.");
+                        pw.println("ProtoLog impl doesn't support handling commands");
                     }
                     return result;
                 case "user-rotation":
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1106a95..46bac16 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -695,7 +695,8 @@
      */
     private boolean mDrawnStateEvaluated;
 
-    private final Point mSurfacePosition = new Point();
+    /** The surface position relative to the parent container. */
+    final Point mSurfacePosition = new Point();
 
     /**
      * A region inside of this window to be excluded from touch.
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 0b58543..b32c544 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -1112,6 +1112,8 @@
         )
         enforceCallingOrSelfAnyPermission(
             "getAllPermissionStates",
+            Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
             Manifest.permission.GET_RUNTIME_PERMISSIONS
         )
 
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 66e0717..c54a94e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -83,11 +83,7 @@
         }
 
         val bundle = service.getUriRelativeFilterGroups(PKG_ONE, listOf(DOMAIN_1, DOMAIN_2))
-        assertThat(bundle.keySet()).containsExactlyElementsIn(listOf(DOMAIN_1, DOMAIN_2))
-        assertThat(bundle.getParcelableArrayList(DOMAIN_1, UriRelativeFilterGroup::class.java))
-            .isEmpty()
-        assertThat(bundle.getParcelableArrayList(DOMAIN_2, UriRelativeFilterGroup::class.java))
-            .isEmpty()
+        assertThat(bundle.keySet()).isEmpty()
 
         val pathGroup = UriRelativeFilterGroup(UriRelativeFilterGroup.ACTION_ALLOW)
         pathGroup.addUriRelativeFilter(
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index fb47aa8..9d3caa5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -98,8 +98,6 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
-    final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[1];
-
     @Mock
     AppOpsService mAppOpsService;
     @Mock
@@ -120,6 +118,7 @@
     HandlerThread mHandlerThread;
     TestLooperManager mLooper;
     AtomicInteger mNextPid;
+    BroadcastHistory mEmptyHistory;
 
     /**
      * Map from PID to registered registered runtime receivers.
@@ -137,6 +136,13 @@
                 .acquireLooperManager(mHandlerThread.getLooper()));
         mNextPid = new AtomicInteger(100);
 
+        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+        mEmptyHistory = new BroadcastHistory(mConstants) {
+            public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+                // Ignored
+            }
+        };
+
         LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
         LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
@@ -164,8 +170,6 @@
         mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
         doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
         doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
-        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
     }
 
     public void tearDown() throws Exception {
@@ -213,8 +217,8 @@
         }
 
         @Override
-        public BroadcastQueue[] getBroadcastQueues(ActivityManagerService service) {
-            return mBroadcastQueues;
+        public BroadcastQueue getBroadcastQueue(ActivityManagerService service) {
+            return null;
         }
     }
 
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 cc6fc80..bcf297f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -118,15 +118,9 @@
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
         mConstants.DELAY_CACHED_MILLIS = 120_000;
 
-        final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
-            public void addBroadcastToHistoryLocked(BroadcastRecord original) {
-                // Ignored
-            }
-        };
-
         mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
-            mConstants, mConstants, mSkipPolicy, emptyHistory);
-        mBroadcastQueues[0] = mImpl;
+                mConstants, mConstants, mSkipPolicy, mEmptyHistory);
+        mAms.setBroadcastQueueForTest(mImpl);
 
         doReturn(1L).when(mQueue1).getRunnableAt();
         doReturn(2L).when(mQueue2).getRunnableAt();
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 e141faf..56e5bd6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -85,12 +85,8 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
 import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
 import org.mockito.verification.VerificationMode;
@@ -100,7 +96,6 @@
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -114,18 +109,10 @@
  * Common tests for {@link BroadcastQueue} implementations.
  */
 @MediumTest
-@RunWith(Parameterized.class)
 @SuppressWarnings("GuardedBy")
 public class BroadcastQueueTest extends BaseBroadcastQueueTest {
     private static final String TAG = "BroadcastQueueTest";
 
-    private final Impl mImpl;
-
-    private enum Impl {
-        DEFAULT,
-        MODERN,
-    }
-
     private BroadcastQueue mQueue;
     private UidObserver mUidObserver;
 
@@ -157,15 +144,6 @@
      */
     private List<Pair<Integer, String>> mScheduledBroadcasts = new ArrayList<>();
 
-    @Parameters(name = "impl={0}")
-    public static Collection<Object[]> data() {
-        return Arrays.asList(new Object[][] { {Impl.DEFAULT}, {Impl.MODERN} });
-    }
-
-    public BroadcastQueueTest(Impl impl) {
-        mImpl = impl;
-    }
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -251,24 +229,9 @@
         }).when(mAms).registerUidObserver(any(), anyInt(),
                 eq(ActivityManager.PROCESS_STATE_TOP), any());
 
-        final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
-            public void addBroadcastToHistoryLocked(BroadcastRecord original) {
-                // Ignored
-            }
-        };
-
-        if (mImpl == Impl.DEFAULT) {
-            mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
-                    mConstants, mSkipPolicy, emptyHistory, false,
-                    ProcessList.SCHED_GROUP_DEFAULT);
-        } else if (mImpl == Impl.MODERN) {
-            mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
-                    mConstants, mConstants, mSkipPolicy, emptyHistory);
-        } else {
-            throw new UnsupportedOperationException();
-        }
-        mBroadcastQueues[0] = mQueue;
-
+        mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+                mConstants, mConstants, mSkipPolicy, mEmptyHistory);
+        mAms.setBroadcastQueueForTest(mQueue);
         mQueue.start(mContext.getContentResolver());
 
         // Set the constants after invoking BroadcastQueue.start() to ensure they don't
@@ -489,10 +452,8 @@
     }
 
     private void assertHealth() {
-        if (mImpl == Impl.MODERN) {
-            // If this fails, it'll throw a clear reason message
-            ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
-        }
+        // If this fails, it'll throw a clear reason message
+        ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
     }
 
     private static Map<String, Object> asMap(Bundle bundle) {
@@ -814,18 +775,13 @@
                     .setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
             verify(mAms, times(2)).enqueueOomAdjTargetLocked(eq(receiverApp));
 
-            if ((mImpl == Impl.DEFAULT) && (receiverApp == receiverBlueApp)) {
-                // Nuance: the default implementation doesn't ask for manifest
-                // cold-started apps to be thawed, but the modern stack does
-            } else {
-                // Confirm that app was thawed
-                verify(mAms.mOomAdjuster, atLeastOnce()).unfreezeTemporarily(
-                        eq(receiverApp), eq(OOM_ADJ_REASON_START_RECEIVER));
+            // Confirm that app was thawed
+            verify(mAms.mOomAdjuster, atLeastOnce()).unfreezeTemporarily(
+                    eq(receiverApp), eq(OOM_ADJ_REASON_START_RECEIVER));
 
-                // Confirm that we added package to process
-                verify(receiverApp, atLeastOnce()).addPackage(eq(receiverApp.info.packageName),
-                        anyLong(), any());
-            }
+            // Confirm that we added package to process
+            verify(receiverApp, atLeastOnce()).addPackage(eq(receiverApp.info.packageName),
+                    anyLong(), any());
 
             // Confirm that we've reported package as being used
             verify(mAms, atLeastOnce()).notifyPackageUse(eq(receiverApp.info.packageName),
@@ -868,9 +824,6 @@
      */
     @Test
     public void testWedged_Registered_Ordered() throws Exception {
-        // Legacy stack doesn't detect these ANRs; likely an oversight
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
                 ProcessBehavior.WEDGE);
@@ -891,9 +844,6 @@
      */
     @Test
     public void testWedged_Registered_ResultTo() throws Exception {
-        // Legacy stack doesn't detect these ANRs; likely an oversight
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
                 ProcessBehavior.WEDGE);
@@ -994,9 +944,7 @@
                 getUidForPackage(PACKAGE_GREEN));
         // Modern queue always kills the target process when broadcast delivery fails, where as
         // the legacy queue leaves the process killing task to AMS
-        if (mImpl == Impl.MODERN) {
-            assertNull(receiverGreenApp);
-        }
+        assertNull(receiverGreenApp);
         final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE,
                 getUidForPackage(PACKAGE_BLUE));
         verifyScheduleReceiver(receiverBlueApp, airplane);
@@ -1110,10 +1058,8 @@
         waitForIdle();
         // Legacy stack does not remove registered receivers as part of
         // cleanUpDisabledPackageReceiversLocked() call, so verify this only on modern queue.
-        if (mImpl == Impl.MODERN) {
-            verifyScheduleReceiver(never(), callerApp, USER_GUEST);
-            verifyScheduleRegisteredReceiver(never(), callerApp, USER_GUEST);
-        }
+        verifyScheduleReceiver(never(), callerApp, USER_GUEST);
+        verifyScheduleRegisteredReceiver(never(), callerApp, USER_GUEST);
         for (String pkg : new String[] {
                 PACKAGE_GREEN, PACKAGE_BLUE, PACKAGE_YELLOW
         }) {
@@ -1199,9 +1145,6 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_AVOID_REPEATED_BCAST_RE_ENQUEUES)
     public void testRepeatedKillWithoutNotify() throws Exception {
-        // Legacy queue does not handle repeated kills that don't get notified.
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
 
@@ -1227,9 +1170,7 @@
 
         // Modern queue always kills the target process when broadcast delivery fails, where as
         // the legacy queue leaves the process killing task to AMS
-        if (mImpl == Impl.MODERN) {
-            assertNull(receiverGreenApp);
-        }
+        assertNull(receiverGreenApp);
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
         verifyScheduleReceiver(times(1), receiverYellowApp, airplane);
         verifyScheduleReceiver(times(1), receiverOrangeApp, timezone);
@@ -1273,11 +1214,7 @@
         assertNotEquals(receiverBlueApp, restartedReceiverBlueApp);
         // Legacy queue will always try delivering the broadcast even if the process
         // has been killed.
-        if (mImpl == Impl.MODERN) {
-            verifyScheduleReceiver(never(), receiverBlueApp, airplane);
-        } else {
-            verifyScheduleReceiver(times(1), receiverBlueApp, airplane);
-        }
+        verifyScheduleReceiver(never(), receiverBlueApp, airplane);
         // Verify that the new process receives the broadcast.
         verifyScheduleReceiver(times(1), restartedReceiverBlueApp, airplane);
     }
@@ -1671,9 +1608,6 @@
      */
     @Test
     public void testPrioritized_withDeferrableBroadcasts() throws Exception {
-        // Legacy stack doesn't support deferral
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
@@ -1834,10 +1768,6 @@
 
     @Test
     public void testReplacePending_withUrgentBroadcast() throws Exception {
-        // The behavior is same with the legacy queue but AMS takes care of finding
-        // the right queue and replacing the broadcast.
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
 
         final Intent timeTickFirst = new Intent(Intent.ACTION_TIME_TICK);
@@ -1903,15 +1833,9 @@
 
         waitForIdle();
 
-        if (mImpl == Impl.MODERN) {
-            verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
-            verifyScheduleRegisteredReceiver(times(2), receiverBlueApp, airplane);
-            verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
-        } else {
-            verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
-            verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
-            verifyScheduleRegisteredReceiver(never(), receiverYellowApp, airplane);
-        }
+        verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
+        verifyScheduleRegisteredReceiver(times(2), receiverBlueApp, airplane);
+        verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
     }
 
     @Test
@@ -1931,14 +1855,10 @@
                 withPriority(receiverGreenA, 5))));
 
         waitForIdle();
-        if (mImpl == Impl.DEFAULT) {
-            verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
-        } else {
-            // In the modern queue, we don't end up replacing the old broadcast to
-            // avoid creating priority inversion and so the process will receive
-            // both the old and new broadcasts.
-            verifyScheduleRegisteredReceiver(times(3), receiverGreenApp, airplane);
-        }
+        // In the modern queue, we don't end up replacing the old broadcast to
+        // avoid creating priority inversion and so the process will receive
+        // both the old and new broadcasts.
+        verifyScheduleRegisteredReceiver(times(3), receiverGreenApp, airplane);
     }
 
     @Test
@@ -1966,11 +1886,7 @@
 
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
-        if (mImpl == Impl.MODERN) {
-            verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
-        } else {
-            verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
-        }
+        verifyScheduleRegisteredReceiver(times(2), receiverGreenApp, airplane);
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane);
     }
 
@@ -2002,9 +1918,6 @@
 
     @Test
     public void testReplacePendingToCachedProcess_withDeferrableBroadcast() throws Exception {
-        // Legacy stack doesn't support deferral
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
@@ -2224,16 +2137,9 @@
         }
         waitForIdle();
 
-        final int expectedTimes;
-        switch (mImpl) {
-            // Original stack requested for every single receiver; yikes
-            case DEFAULT: expectedTimes = 64; break;
-            // Modern stack requests once each time we promote a process to
-            // running; we promote "green" twice, and "blue" and "yellow" once
-            case MODERN: expectedTimes = 4; break;
-            default: throw new UnsupportedOperationException();
-        }
-
+        // Modern stack requests once each time we promote a process to
+        // running; we promote "green" twice, and "blue" and "yellow" once
+        final int expectedTimes = 4;
         verify(mAms, times(expectedTimes))
                 .updateOomAdjPendingTargetsLocked(eq(OOM_ADJ_REASON_START_RECEIVER));
     }
@@ -2302,9 +2208,6 @@
      */
     @Test
     public void testDeferralPolicy_UntilActive() throws Exception {
-        // Legacy stack doesn't support deferral
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
@@ -2350,9 +2253,6 @@
      */
     @Test
     public void testDeferralPolicy_UntilActive_WithMultiProcessUid() throws Exception {
-        // Legacy stack doesn't support deferral
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverGreenApp1 = makeActiveProcessRecord(PACKAGE_GREEN);
         final ProcessRecord receiverGreenApp2 = makeActiveProcessRecord(PACKAGE_GREEN,
@@ -2384,9 +2284,6 @@
 
     @Test
     public void testBroadcastDelivery_uidForeground() throws Exception {
-        // Legacy stack doesn't support prioritization to foreground app.
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
         final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
@@ -2418,9 +2315,6 @@
 
     @Test
     public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception {
-        // Legacy stack doesn't support prioritization to foreground app.
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
         final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index f0efb79..878b945 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -61,8 +61,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.am.BroadcastDispatcher.DeferredBootCompletedBroadcastPerUser;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -747,68 +745,6 @@
         }
     }
 
-    /**
-     * Test the class {@link BroadcastDispatcher#DeferredBootCompletedBroadcastPerUser}
-     */
-    @Test
-    public void testDeferBootCompletedBroadcast_dispatcher() {
-        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, false);
-        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, false);
-        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_LOCKED_BOOT_COMPLETED, true);
-        testDeferBootCompletedBroadcast_dispatcher_internal(ACTION_BOOT_COMPLETED, true);
-    }
-
-    private void testDeferBootCompletedBroadcast_dispatcher_internal(String action,
-            boolean isAllUidReady) {
-        final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, new int[] {USER0});
-        final BroadcastRecord br = createBroadcastRecord(receivers, USER0, new Intent(action));
-        assertEquals(PACKAGE_LIST.length, br.receivers.size());
-
-        SparseArray<BroadcastRecord> deferred = br.splitDeferredBootCompletedBroadcastLocked(
-                mActivityManagerInternal, DEFER_BOOT_COMPLETED_BROADCAST_ALL);
-        // original BroadcastRecord receivers list is empty now.
-        assertTrue(br.receivers.isEmpty());
-        assertEquals(PACKAGE_LIST.length, deferred.size());
-
-        DeferredBootCompletedBroadcastPerUser deferredPerUser =
-                new DeferredBootCompletedBroadcastPerUser(USER0);
-        deferredPerUser.enqueueBootCompletedBroadcasts(action, deferred);
-
-        if (action.equals(ACTION_LOCKED_BOOT_COMPLETED)) {
-            assertEquals(PACKAGE_LIST.length,
-                    deferredPerUser.mDeferredLockedBootCompletedBroadcasts.size());
-            assertTrue(deferredPerUser.mLockedBootCompletedBroadcastReceived);
-            for (int i = 0; i < PACKAGE_LIST.length; i++) {
-                final int uid = UserHandle.getUid(USER0, getAppId(i));
-                if (!isAllUidReady) {
-                    deferredPerUser.updateUidReady(uid);
-                }
-                BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
-                        isAllUidReady);
-                final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
-                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
-                assertEquals(uid, info.activityInfo.applicationInfo.uid);
-            }
-            assertEquals(0, deferredPerUser.mUidReadyForLockedBootCompletedBroadcast.size());
-        } else if (action.equals(ACTION_BOOT_COMPLETED)) {
-            assertEquals(PACKAGE_LIST.length,
-                    deferredPerUser.mDeferredBootCompletedBroadcasts.size());
-            assertTrue(deferredPerUser.mBootCompletedBroadcastReceived);
-            for (int i = 0; i < PACKAGE_LIST.length; i++) {
-                final int uid = UserHandle.getUid(USER0, getAppId(i));
-                if (!isAllUidReady) {
-                    deferredPerUser.updateUidReady(uid);
-                }
-                BroadcastRecord d = deferredPerUser.dequeueDeferredBootCompletedBroadcast(
-                        isAllUidReady);
-                final ResolveInfo info = (ResolveInfo) d.receivers.get(0);
-                assertEquals(PACKAGE_LIST[i], info.activityInfo.applicationInfo.packageName);
-                assertEquals(uid, info.activityInfo.applicationInfo.uid);
-            }
-            assertEquals(0, deferredPerUser.mUidReadyForBootCompletedBroadcast.size());
-        }
-    }
-
     private static void cleanupDisabledPackageReceivers(BroadcastRecord record,
             String packageName, int userId) {
         record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1226f0c..872ac40 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -297,6 +297,10 @@
         } else {
             updateProcessRecordNodes(Arrays.asList(apps));
             if (apps.length == 1) {
+                final ProcessRecord app = apps[0];
+                if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) {
+                    sService.mProcessList.getLruProcessesLOSP().add(app);
+                }
                 sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
             } else {
                 setProcessesToLru(apps);
@@ -475,7 +479,16 @@
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, CACHED_APP_MIN_ADJ,
+        final int expectedAdj;
+        if (sService.mConstants.ENABLE_NEW_OOMADJ) {
+            // A cached empty process can be at best a level higher than the min cached adj.
+            expectedAdj = sFirstCachedAdj;
+        } else {
+            // This is wrong but legacy behavior is going to be removed and not worth fixing.
+            expectedAdj = CACHED_APP_MIN_ADJ;
+        }
+
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj,
                 SCHED_GROUP_BACKGROUND);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 7bbcd50..bc7c9a5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.os.UserManager.DISALLOW_OUTGOING_CALLS;
 import static android.os.UserManager.DISALLOW_SMS;
 import static android.os.UserManager.DISALLOW_USER_SWITCH;
@@ -41,6 +45,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -48,6 +53,7 @@
 import android.content.pm.UserInfo;
 import android.multiuser.Flags;
 import android.os.PowerManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -76,6 +82,7 @@
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -120,11 +127,14 @@
 
     private static final String TAG_RESTRICTIONS = "restrictions";
 
+    private static final String PRIVATE_PROFILE_NAME = "TestPrivateProfile";
+
     @Rule
     public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
             .spyStatic(UserManager.class)
             .spyStatic(LocalServices.class)
             .spyStatic(SystemProperties.class)
+            .spyStatic(ActivityManager.class)
             .mockStatic(Settings.Global.class)
             .mockStatic(Settings.Secure.class)
             .build();
@@ -163,6 +173,7 @@
     @Before
     @UiThreadTest // Needed to initialize main handler
     public void setFixtures() {
+        MockitoAnnotations.initMocks(this);
         mSpiedContext = spy(mRealContext);
 
         // Called when WatchedUserStates is constructed
@@ -172,11 +183,12 @@
         when(mDeviceStorageMonitorInternal.isMemoryLow()).thenReturn(false);
         mockGetLocalService(DeviceStorageMonitorInternal.class, mDeviceStorageMonitorInternal);
         when(mSpiedContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
-        when(mSpiedContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
+        doReturn(mKeyguardManager).when(mSpiedContext).getSystemService(KeyguardManager.class);
         when(mSpiedContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
         mockGetLocalService(LockSettingsInternal.class, mLockSettingsInternal);
         mockGetLocalService(PackageManagerInternal.class, mPackageManagerInternal);
         doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
+        mockIsLowRamDevice(false);
 
         // Must construct UserManagerService in the UiThread
         mTestDir = new File(mRealContext.getDataDir(), "umstest");
@@ -570,9 +582,10 @@
 
     @Test
     public void testAutoLockPrivateProfile() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         UserManagerService mSpiedUms = spy(mUms);
         UserInfo privateProfileUser =
-                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                         USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
         Mockito.doNothing().when(mSpiedUms).setQuietModeEnabledAsync(
                 eq(privateProfileUser.getUserHandle().getIdentifier()), eq(true), any(),
@@ -587,10 +600,11 @@
 
     @Test
     public void testAutoLockOnDeviceLockForPrivateProfile() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
         UserManagerService mSpiedUms = spy(mUms);
         UserInfo privateProfileUser =
-                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                 USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
         mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
         Mockito.doNothing().when(mSpiedUms).setQuietModeEnabledAsync(
@@ -606,10 +620,11 @@
 
     @Test
     public void testAutoLockOnDeviceLockForPrivateProfile_keyguardUnlocked() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
         UserManagerService mSpiedUms = spy(mUms);
         UserInfo privateProfileUser =
-                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                 USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
         mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK);
 
@@ -623,10 +638,11 @@
 
     @Test
     public void testAutoLockOnDeviceLockForPrivateProfile_flagDisabled() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.disableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
         UserManagerService mSpiedUms = spy(mUms);
         UserInfo privateProfileUser =
-                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                 USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
 
         mSpiedUms.tryAutoLockingPrivateSpaceOnKeyguardChanged(true);
@@ -641,13 +657,14 @@
 
     @Test
     public void testAutoLockAfterInactityForPrivateProfile() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
         UserManagerService mSpiedUms = spy(mUms);
         mockAutoLockForPrivateSpace(Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY);
         when(mPowerManager.isInteractive()).thenReturn(false);
 
         UserInfo privateProfileUser =
-                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+                mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                         USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
         Mockito.doNothing().when(mSpiedUms).scheduleMessageToAutoLockPrivateSpace(
                 eq(privateProfileUser.getUserHandle().getIdentifier()), any(),
@@ -662,6 +679,7 @@
 
     @Test
     public void testSetOrUpdateAutoLockPreference_noPrivateProfile() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
 
         mUms.setOrUpdateAutoLockPreferenceForPrivateProfile(
@@ -675,8 +693,9 @@
 
     @Test
     public void testSetOrUpdateAutoLockPreference() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
         mSetFlagsRule.enableFlags(Flags.FLAG_SUPPORT_AUTOLOCK_FOR_PRIVATE_SPACE);
-        mUms.createProfileForUserEvenWhenDisallowedWithThrow("TestPrivateProfile",
+        mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
                         USER_TYPE_PROFILE_PRIVATE, 0, 0, null);
 
         // Set the preference to auto lock on device lock
@@ -733,6 +752,77 @@
         }
     }
 
+    @Test
+    public void testCreatePrivateProfileOnHeadlessSystemUser_shouldAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        UserManagerService mSpiedUms = spy(mUms);
+        int mainUser = mSpiedUms.getMainUserId();
+        doReturn(true).when(mSpiedUms).isHeadlessSystemUserMode();
+        assertThat(mSpiedUms.canAddPrivateProfile(mainUser)).isTrue();
+        assertThat(mSpiedUms.createProfileForUserEvenWhenDisallowedWithThrow(
+                PRIVATE_PROFILE_NAME, USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null)).isNotNull();
+    }
+
+    @Test
+    public void testCreatePrivateProfileOnSecondaryUser_shouldNotAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0);
+        assertThat(mUms.canAddPrivateProfile(user.id)).isFalse();
+        assertThrows(ServiceSpecificException.class,
+                () -> mUms.createProfileForUserWithThrow(PRIVATE_PROFILE_NAME,
+                        USER_TYPE_PROFILE_PRIVATE, 0, user.id, null));
+    }
+
+    @Test
+    public void testCreatePrivateProfileOnAutoDevices_shouldNotAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_AUTOMOTIVE), anyInt());
+        int mainUser = mUms.getMainUserId();
+        assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+        assertThrows(ServiceSpecificException.class,
+                () -> mUms.createProfileForUserWithThrow(PRIVATE_PROFILE_NAME,
+                        USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
+    }
+
+    @Test
+    public void testCreatePrivateProfileOnTV_shouldNotAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_LEANBACK), anyInt());
+        int mainUser = mUms.getMainUserId();
+        assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+        assertThrows(ServiceSpecificException.class,
+                () -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
+                        USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
+    }
+
+    @Test
+    public void testCreatePrivateProfileOnEmbedded_shouldNotAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_EMBEDDED), anyInt());
+        int mainUser = mUms.getMainUserId();
+        assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+        assertThrows(ServiceSpecificException.class,
+                () -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
+                        USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
+    }
+
+    @Test
+    public void testCreatePrivateProfileOnWatch_shouldNotAllowCreation() {
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_BLOCK_PRIVATE_SPACE_CREATION);
+        doReturn(true).when(mMockPms).hasSystemFeature(eq(FEATURE_WATCH), anyInt());
+        int mainUser = mUms.getMainUserId();
+        assertThat(mUms.canAddPrivateProfile(0)).isFalse();
+        assertThrows(ServiceSpecificException.class,
+                () -> mUms.createProfileForUserEvenWhenDisallowedWithThrow(PRIVATE_PROFILE_NAME,
+                        USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
+    }
+
     /**
      * Returns true if the user's XML file has Default restrictions
      * @param userId Id of the user.
@@ -800,6 +890,10 @@
                 any(), eq(android.provider.Settings.Global.USER_SWITCHER_ENABLED), anyInt()));
     }
 
+    private void mockIsLowRamDevice(boolean isLowRamDevice) {
+        doReturn(isLowRamDevice).when(ActivityManager::isLowRamDeviceStatic);
+    }
+
     private void mockDeviceDemoMode(boolean enabled) {
         doReturn(enabled ? 1 : 0).when(() -> Settings.Global.getInt(
                 any(), eq(android.provider.Settings.Global.DEVICE_DEMO_MODE), anyInt()));
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5e5181b..0089d4c 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -109,7 +109,6 @@
     <uses-permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" />
     <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" />
     <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
-    <uses-permission android:name="android.permission.USE_BACKGROUND_FACE_AUTHENTICATION" />
     <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
 
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index cea10ea..ea1a68a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -825,7 +825,8 @@
         mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
         assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
         verifyUserUnassignedFromDisplay(TEST_USER_ID1);
@@ -842,7 +843,8 @@
         mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
         assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* allowDelayedLocking= */ true,
                 /* keyEvictedCallback */ null, /* expectLocking= */ false);
@@ -852,19 +854,28 @@
     public void testStopPrivateProfileWithDelayedLocking_flagDisabled() throws Exception {
         mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
-        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         mSetFlagsRule.disableFlags(
                 android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
         setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
         assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* allowDelayedLocking= */ true,
                 /* keyEvictedCallback */ null, /* expectLocking= */ true);
 
-        mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         mSetFlagsRule.enableFlags(
                 android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
         setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE);
         assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* allowDelayedLocking= */ true,
                 /* keyEvictedCallback */ null, /* expectLocking= */ true);
+
+        mSetFlagsRule.disableFlags(android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
+        mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+        setUpAndStartProfileInBackground(TEST_USER_ID3, UserManager.USER_TYPE_PROFILE_PRIVATE);
+        assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* allowDelayedLocking= */ true,
+                /* keyEvictedCallback */ null, /* expectLocking= */ true);
     }
 
     /** Delayed-locking users (as opposed to devices) have no limits on how many can be unlocked. */
@@ -874,7 +885,8 @@
         mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
                 /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false);
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE);
         setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_MANAGED);
         assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* allowDelayedLocking= */ true,
@@ -890,7 +902,8 @@
         mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true,
                 /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
         mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
-                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
+                android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED);
         assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* allowDelayedLocking= */ true,
                 /* keyEvictedCallback */ null, /* expectLocking= */ true);
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt b/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt
new file mode 100644
index 0000000..79766f8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.appwidget
+
+import android.content.ComponentName
+import com.android.server.appwidget.AppWidgetServiceImpl.ApiCounter
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ApiCounterTest {
+    private companion object {
+        const val RESET_INTERVAL_MS = 10L
+        const val MAX_CALLS_PER_INTERVAL = 2
+    }
+
+    private var currentTime = 0L
+
+    private val id =
+        AppWidgetServiceImpl.ProviderId(
+            /* uid= */ 123,
+            ComponentName("com.android.server.appwidget", "FakeProviderClass")
+        )
+    private val counter = ApiCounter(RESET_INTERVAL_MS, MAX_CALLS_PER_INTERVAL) { currentTime }
+
+    @Test
+    fun tryApiCall() {
+        for (i in 0 until MAX_CALLS_PER_INTERVAL) {
+            assertThat(counter.tryApiCall(id)).isTrue()
+        }
+        assertThat(counter.tryApiCall(id)).isFalse()
+        currentTime = 5L
+        assertThat(counter.tryApiCall(id)).isFalse()
+        currentTime = 11L
+        assertThat(counter.tryApiCall(id)).isTrue()
+    }
+
+    @Test
+    fun remove() {
+        for (i in 0 until MAX_CALLS_PER_INTERVAL) {
+            assertThat(counter.tryApiCall(id)).isTrue()
+        }
+        assertThat(counter.tryApiCall(id)).isFalse()
+        // remove should cause the call count to be 0 on the next tryApiCall
+        counter.remove(id)
+        assertThat(counter.tryApiCall(id)).isTrue()
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
index c8a5583d..3aaac2e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.biometrics.sensors.face;
 
-import static android.Manifest.permission.USE_BACKGROUND_FACE_AUTHENTICATION;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
 import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN;
@@ -235,26 +234,6 @@
     }
 
     @Test
-    public void testAuthenticateInBackground() throws Exception {
-        FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
-                .build();
-        initService();
-        mFaceService.mServiceWrapper.registerAuthenticators(List.of());
-        waitForRegistration();
-
-        mContext.getTestablePermissions().setPermission(
-                USE_BIOMETRIC_INTERNAL, PackageManager.PERMISSION_DENIED);
-        mContext.getTestablePermissions().setPermission(
-                USE_BACKGROUND_FACE_AUTHENTICATION, PackageManager.PERMISSION_GRANTED);
-
-        final long operationId = 5;
-        mFaceService.mServiceWrapper.authenticateInBackground(mToken, operationId,
-                mFaceServiceReceiver, faceAuthenticateOptions);
-
-        assertThat(faceAuthenticateOptions.getSensorId()).isEqualTo(ID_DEFAULT);
-    }
-
-    @Test
     public void testOptionsForDetect() throws Exception {
         FaceAuthenticateOptions faceAuthenticateOptions = new FaceAuthenticateOptions.Builder()
                 .setOpPackageName(ComponentName.unflattenFromString(OP_PACKAGE_NAME)
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index 9ad2652..6731403 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -235,7 +235,7 @@
     }
 
     @Test
-    public void adjustOnlyAvbEnabled_audioDeviceVolumeChanged_doesNotSendSetAudioVolumeLevel() {
+    public void adjustOnlyAvbEnabled_audioDeviceVolumeChanged_requestsAndUpdatesAudioStatus() {
         enableAdjustOnlyAbsoluteVolumeBehavior();
 
         mNativeWrapper.clearResultMessages();
@@ -250,7 +250,22 @@
         );
         mTestLooper.dispatchAll();
 
-        assertThat(mNativeWrapper.getResultMessages()).isEmpty();
+        // We can't sent <Set Audio Volume Level> when using adjust-only AVB.
+        // Instead, we send <Give Audio Status>, to get the System Audio device's volume level.
+        // This ensures that we end up with a correct audio status in AudioService, even if it
+        // set it incorrectly because it assumed that we could send <Set Audio Volume Level>
+        assertThat(mNativeWrapper.getResultMessages().size()).isEqualTo(1);
+        assertThat(mNativeWrapper.getResultMessages()).contains(
+                HdmiCecMessageBuilder.buildGiveAudioStatus(getLogicalAddress(),
+                        getSystemAudioDeviceLogicalAddress())
+        );
+
+        // When we receive <Report Audio Status>, we notify AudioService of the volume level.
+        receiveReportAudioStatus(50,
+                true);
+        verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC),
+                eq(50 * STREAM_MUSIC_MAX_VOLUME / AudioStatus.MAX_VOLUME),
+                anyInt());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 0aa72d0..98e119c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -184,13 +184,13 @@
 
     @Test
     public void isValid_setMenuLanguage() {
-        assertMessageValidity("4F:32:53:50:41").isEqualTo(OK);
+        assertMessageValidity("0F:32:53:50:41").isEqualTo(OK);
         assertMessageValidity("0F:32:45:4E:47:8C:49:D3:48").isEqualTo(OK);
 
-        assertMessageValidity("40:32:53:50:41").isEqualTo(ERROR_DESTINATION);
-        assertMessageValidity("F0:32").isEqualTo(ERROR_SOURCE);
-        assertMessageValidity("4F:32:45:55").isEqualTo(ERROR_PARAMETER_SHORT);
-        assertMessageValidity("4F:32:19:7F:83").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:32:53:50:41").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("40:32").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("0F:32:45:55").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("0F:32:19:7F:83").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 507b3fe..1591a96 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -316,6 +316,10 @@
                 .that(userTypeDetails).isNotNull();
         final UserProperties typeProps = userTypeDetails.getDefaultUserPropertiesReference();
 
+        // Only run the test if private profile creation is enabled on the device
+        assumeTrue("Private profile not enabled on the device",
+                mUserManager.canAddPrivateProfile());
+
         // Test that only one private profile  can be created
         final int mainUserId = mainUser.getIdentifier();
         UserInfo userInfo = createProfileForUser("Private profile1",
@@ -1231,6 +1235,20 @@
 
     @MediumTest
     @Test
+    public void testPrivateProfileCreationRestrictions() {
+        assumeTrue(mUserManager.canAddPrivateProfile());
+        final int mainUserId = ActivityManager.getCurrentUser();
+        try {
+            UserInfo privateProfileInfo = createProfileForUser("Private",
+                            UserManager.USER_TYPE_PROFILE_PRIVATE, mainUserId);
+            assertThat(privateProfileInfo).isNotNull();
+        } catch (Exception e) {
+            fail("Creation of private profile failed due to " + e.getMessage());
+        }
+    }
+
+    @MediumTest
+    @Test
     public void testAddRestrictedProfile() throws Exception {
         if (isAutomotive() || UserManager.isHeadlessSystemUserMode()) return;
         assertWithMessage("There should be no associated restricted profiles before the test")
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e3ea55a..03f2749 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -14092,7 +14092,8 @@
 
     @Test
     public void testProfileUnavailableIntent() throws RemoteException {
-        mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         simulateProfileAvailabilityActions(Intent.ACTION_PROFILE_UNAVAILABLE);
         verify(mWorkerHandler).post(any(Runnable.class));
         verify(mSnoozeHelper).clearData(anyInt());
@@ -14101,7 +14102,8 @@
 
     @Test
     public void testManagedProfileUnavailableIntent() throws RemoteException {
-        mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE);
+        mSetFlagsRule.disableFlags(FLAG_ALLOW_PRIVATE_PROFILE,
+                android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES);
         simulateProfileAvailabilityActions(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
         verify(mWorkerHandler).post(any(Runnable.class));
         verify(mSnoozeHelper).clearData(anyInt());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 7c1adbc..c4d2460 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -4808,6 +4808,53 @@
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
+    public void updateAutomaticZenRule_ruleChanged_deactivatesRule() {
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
+                .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, UPDATE_ORIGIN_APP, "reason",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        AutomaticZenRule updateWithDiff = new AutomaticZenRule.Builder(rule)
+                .setTriggerDescription("Whenever")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, updateWithDiff, UPDATE_ORIGIN_APP, "reason",
+                CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() {
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
+                .setConfigurationActivity(new ComponentName(mPkg, "cls"))
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, UPDATE_ORIGIN_APP, "reason",
+                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleState(ruleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        AutomaticZenRule updateUnchanged = new AutomaticZenRule.Builder(rule).build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, updateUnchanged, UPDATE_ORIGIN_APP, "reason",
+                CUSTOM_PKG_UID);
+
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(ruleId).condition).isEqualTo(
+                CONDITION_TRUE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
     public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         reset(mDeviceEffectsApplier);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index f54c7e5..88a9483 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -604,7 +604,8 @@
     @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
     public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
-        setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0);
+        setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
+        setHasFixedKeyboardAmplitudeIntensity(true);
 
         // Keyboard touch ignored.
         assertVibrationIgnoredForAttributes(
@@ -628,7 +629,8 @@
     @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
     public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
-        setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1);
+        setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
+        setHasFixedKeyboardAmplitudeIntensity(true);
 
         // General touch ignored.
         assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -642,6 +644,25 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
+    public void shouldIgnoreVibration_noFixedKeyboardAmplitude_ignoresKeyboardTouchVibration() {
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
+        setHasFixedKeyboardAmplitudeIntensity(false);
+
+        // General touch ignored.
+        assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+
+        // Keyboard touch ignored.
+        assertVibrationIgnoredForAttributes(
+                new VibrationAttributes.Builder()
+                        .setUsage(USAGE_TOUCH)
+                        .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                        .build(),
+                Vibration.Status.IGNORED_FOR_SETTINGS);
+    }
+
+    @Test
     public void shouldIgnoreVibrationFromVirtualDevices_defaultDevice_neverIgnored() {
         // Vibrations from the primary device is never ignored.
         for (int usage : ALL_USAGES) {
@@ -953,6 +974,10 @@
         when(mVibrationConfigMock.ignoreVibrationsOnWirelessCharger()).thenReturn(ignore);
     }
 
+    private void setHasFixedKeyboardAmplitudeIntensity(boolean hasFixedAmplitude) {
+        when(mVibrationConfigMock.hasFixedKeyboardAmplitude()).thenReturn(hasFixedAmplitude);
+    }
+
     private void deleteUserSetting(String settingName) {
         Settings.System.putStringForUser(
                 mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 09e7b91..45a2ba4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3147,12 +3147,12 @@
         // By default, activity is visible.
         assertTrue(activity.isVisible());
         assertTrue(activity.isVisibleRequested());
-        assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
         assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
 
         // Request the activity to be visible. Although the activity is already visible, app
         // transition animation should be applied on this activity. This might be unnecessary, but
         // until we verify no logic relies on this behavior, we'll keep this as is.
+        mDisplayContent.prepareAppTransition(0);
         activity.setVisibility(true);
         assertTrue(activity.isVisible());
         assertTrue(activity.isVisibleRequested());
@@ -3167,11 +3167,11 @@
         // By default, activity is visible.
         assertTrue(activity.isVisible());
         assertTrue(activity.isVisibleRequested());
-        assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
         assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
 
         // Request the activity to be invisible. Since the visibility changes, app transition
         // animation should be applied on this activity.
+        mDisplayContent.prepareAppTransition(0);
         activity.setVisibility(false);
         assertTrue(activity.isVisible());
         assertFalse(activity.isVisibleRequested());
@@ -3187,7 +3187,6 @@
         // activity.
         assertFalse(activity.isVisible());
         assertTrue(activity.isVisibleRequested());
-        assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
         assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
 
         // Request the activity to be visible. Since the visibility changes, app transition
@@ -3389,6 +3388,7 @@
         // frozen until the input started.
         mDisplayContent.setImeLayeringTarget(app1);
         mDisplayContent.updateImeInputAndControlTarget(app1);
+        mDisplayContent.computeImeTarget(true /* updateImeTarget */);
         performSurfacePlacementAndWaitForWindowAnimator();
 
         assertEquals(app1, mDisplayContent.getImeInputTarget());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 527ea0d..ce90504 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -1239,7 +1239,7 @@
         final ActivityRecord activity1 = finishTopActivity(rootTask1);
         assertEquals(DESTROYING, activity1.getState());
         verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
-                eq(display.mDisplayId), anyBoolean());
+                eq(display), anyBoolean());
     }
 
     private ActivityRecord finishTopActivity(Task task) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index da11e6a..649f520 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -835,8 +835,6 @@
                 new TestDisplayContent.Builder(mAtm, 1000, 1500)
                         .setSystemDecorations(true).build();
 
-        doReturn(true).when(mRootWindowContainer)
-                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean());
         doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplayArea(any(), any(),
                 anyBoolean());
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 8b44579..36adeec 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -380,6 +380,11 @@
             return false;
         }
 
+        if (descriptors == null) {
+            Slog.e(TAG, "Failed to add device as the descriptor is null");
+            return false;
+        }
+
         UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors);
         if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE
                 && !checkUsbInterfacesDenyListed(parser)) {
@@ -462,8 +467,7 @@
                 }
 
                 // Tracking
-                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
-                        parser.getRawDescriptors());
+                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT, descriptors);
 
                 // Stats collection
                 FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,